Modifying Mat attribute in class with mouse callback - c++

Going to explain using bullets to make it easy to read (hopefully):
I'm writing a program which needs to be able to draw on top of images, using the mouse.
The way I have organized the program is that each image is stored in it's own instance of a class. The instance includes a cv::Mat attribute where the image is saved, and a blank cv::Mat (I refer to as the canvas) where I want whatever is drawn to be saved. The canvas is the same size and type as the image cv::Mat.
I've written a mouse callback function to draw rectangles, however I get an error (which I believe is to do with getting the stored canvas from the image).
OpenCV Error: Assertion failed (0 <= _dims && _dims <= CV_MAX_DIM) in setSize, file /tmp/opencv-0tcS7S/opencv-2.4.9/modules/core/src/matrix.cpp, line 89
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /tmp/opencv-0tcS7S/opencv-2.4.9/modules/core/src/matrix.cpp:89: error: (-215) 0 <= _dims && _dims <= CV_MAX_DIM in function setSize
Here is my code:
void draw(int event, int x, int y, int flags, void* params){
ImgData* input = (ImgData*)params; //Convert the struct passed in as void to a ImageData struct.
if(event == EVENT_LBUTTONDOWN){
printf("Left mouse button clicked.\n");
input->ipt = Point(x,y); //Store the initial point, this point is saved in memory.
printf("Original position = (%i,%i)\n",x,y);
}else if(event == EVENT_LBUTTONUP){
cv::Mat temp_canvas;
input->getCanvas().copyTo(temp_canvas);
printf("Left mouse button released.\n");
input->fpt = Point(x,y); //Final Point.
cv::rectangle(temp_canvas, input->ipt, input->fpt, Scalar(200,0,0));
input->setCanvas(temp_canvas);
}
}
The way I'm trying to do it is to copy the canvas from the object instance, draw the rectangle on this copy, then overwrite the old canvas with this modified canvas.
Any help with this or explanation as to why it is happening will be greatly appreciated.
Thank you

I've solved the problem, was simply that I wasn't passing the object correctly:
setMouseCallback("Window", draw, &images->at(imageShown)); // replaced the code: setMouseCallback("Window", draw, &images[imageShown]);
Silly mistake...

Related

glReadPixels doesnt work for the first left click

I am working on a MFC app which is a MDI. One of the child frame uses OpenGL(mixed with fixed function and modern version) called 3d view and another child frame uses GDI called plan view. Both of the views use the same doc.
The 3d view has a function to detect if the mouse cursor is over rendered 3d model by reading pixels and check its depth value.
The function is used for WM_MOUSEMOVE and WM_LBUTTONDOWN events. Most time it works pretty well. But it failed when I move my cursor from the plan view(currently active) to the 3d view and left mouse click. The depth values read from the pixels(called from onLButtonDown) are always all zeros though it is over a model. There is no OpenGL error reported. It only fails on the first mouse click when the 3d view is not activated. Afterwards, everything works well again.
The issue doesn't happen on all machines. And it happens to me but not to another guy with the same hardware machine with me. Is that possible hardware related or a code bug?
Things tried:
I tried to increase the pixel block size much bigger but depths are still all zero.
If I click on the title bar of the 3d view to activate it first, then works.
I tried to set the 3d view active and foreground in the onLButtonDown method before reading pixels. But still failed.(btw, the 3d view should be active already before the OnLButtonDown handler via other message handler fired by the left button down).
I tried to invalidate rect before reading pixels, failed too.
The code is as below:
BOOL CMy3DView::IsOverModel(int x0, int y0, int &xM, int &yM, GLfloat &zWin, int width0 , int height0 )
{
int width = max(1,width0);
int height= max(1,height0);
CRect RectView;
GetClientRect(&RectView);
GLint realy = RectView.Height() - 1 - (GLint)y0 ; /* OpenGL y coordinate position */
std::vector<GLfloat> z(width*height);
//Read the window z co-ordinates the z value of the points in a rectangle of width*height )
xM = max(0, x0-(width-1)/2);
yM = max(0, realy-(height-1)/2);
glReadPixels(xM, yM, (GLsizei)width, (GLsizei)height, GL_DEPTH_COMPONENT, GL_FLOAT, &z[0]); OutputGlError(_T("glReadPixels")) ;
/* check pixels along concentric, nested boxes around the central point */
for (int k=0; k<=(min(height,width)-1)/2; ++k){
for (int i=-k;i<=k;++i){
xM = x0+i;
for (int j=-k;j<=k;++j){
if (abs(i)==k || abs(j)==k) {
yM = realy+j;
zWin=z[(i+(width-1)/2)+width*(j+(height-1)/2)];
if (zWin<1.0-FLT_EPSILON) break;
}
}
if (zWin<1.0-FLT_EPSILON) break;
}
if (zWin<1.0-FLT_EPSILON) break;
}
yM = RectView.Height() - 1 - yM;
if (zWin>1.0-FLT_EPSILON || zWin<FLT_EPSILON) {// z is the depth, between 0 and 1, i.e. between Near and Far plans.
xM=x0; yM=y0;
return FALSE;
}
return TRUE;
}
Just found a solution for that: I called render(GetDC) before any processing in OnLButtonDown. somehow it fixed the issue though I don't think it's necessary.
InvalideRect wont fix the issue since it will update the view for the next WM_PAINT.
Weird, since it works for some machines without the fix. Still curious about the reason.

QGraphicsScene.itemAt() only returns zero and whole scene is slow

I am trying to create a dot matrix in a QGraphicsScene. I have a button, which fills a 2d-array with random numbers and than i will paint a pixel on every position where the array has a 0.
Now, when I wants to generate the matrix again i want to check every pixel and array-field whether they are empty or not. If the pixel is empty and the array not, i want to set a pixel. If there is a pixel but the array is empty i want to remove the pixel. Now the problem is, the function itemAt() always returns 0 even if i can clearly see existen pixels.
What is my problem?
//creating the scene in the constructor
QPainter MyPainter(this);
scene = new QGraphicsScene(this);
ui.graphicsView->setScene(scene);
//generating matrix
void MaReSX_ClickDummy::generate(void)
{
QGraphicsItem *item;
int x, y;
for(x=0; x< 400; x++)
{
for(y=0; y<400; y++)
{
dataArray[x][y] = rand()%1001;
}
}
for(x=0; x < 400; x++)
{
for(y=0; y<400; y++)
{
item = scene->itemAt(x, y, QTransform());//supposed to check whether there is already a pixel on that place but always returns zero
if(dataArray[x][y] == 0 && item == 0)
scene->addEllipse(x, y, 1, 1);
//does not work
else if(dataArray[x][y] != 0 && item != 0)
scene->removeItem(item);
}
}
}
Also the generating of the matrix is very slow. Since the matrix is supposed to show realtime data later, it should run as fast as possible. (and the scene will be bigger than 400*400 pixels like now). Any ideas how to improve the code?
And can somebody explain what the third parameter of itemAt() is supposed to do?
Thank you!
400x400 'dot matrix' is up to 16000 dots, or up to 2500 characters, which quite big.
The QGraphicsScene is designed to handle a small number of large shapes, and was probably not designed to handle this many shapes. Using it in this way to create thousands of identical tiny 'pixel' objects is incredibly inefficent.
Could you create a 400x400 bitmap(QBitmap?) instead, and set the individual pixels that you want?
You are supposed to be using a QGraphicsPixmapItem instead of an array of dots!

Using FLTK & C++: Making a function that fits an image within a defined frame

I am writing a program in which the user can type the name of an image, for example "image.jpg". I am trying to devise a function that gets that image, and without cropping it, resizes it in a manner that allows it to fit in a Rectangle shape of 470 x 410 pixels.
Would anyone happen to know how to obtain the numerical values of the size of the image and/or resize the image so that it fits inside that Rectangle?
There's an example program in the FLTK documentation called pixmap_browser.cpp. On my Linux system, I found it under /usr/share/doc/fltk-1.3.2/examples
Here's the essence of the code you're looking for:
#include <FL/Fl_Shared_Image.H>
// ...
// Load the image file:
Fl_Shared_Image *img = Fl_Shared_Image::get(filename);
// Or die:
if (!img) {
return;
}
// Resize the image if it's too big, by replacing it with a resized copy:
if (img->w() > box->w() || img->h() > box->h()) {
Fl_Image *temp;
if (img->w() > img->h()) {
temp = img->copy(box->w(), box->h() * img->h() / img->w());
} else {
temp = img->copy(box->w() * img->w() / img->h(), box->h());
}
img->release();
img = (Fl_Shared_Image *) temp;
}
The method that does the resizing is Fl_Image::copy. Basically, the code replaces the source image with a resized copy if it's too big.

How to pass detected faces from a function to a separate push button slot in Qt

I am using Qt for creating front-end,I have a method called 'facedetect',in that i detect all the faces.Now i need to pass those detected faces to another function,in that it compares detected faces with all the faces in the database.But the actual problem is ,I am comparing faces when i click a pushbutton 'compare'(It is in different slot). I need to access the detected faces in to the 'compare' pushbutton slots.
Here is my code:
void third::facedetect(IplImage* image) //face detection
{
int j=0,l=0,strsize,count=0;
char numstr[50];
CvPoint ul,lr,w,h;
string path;
IplImage* image1;IplImage* tmpsize;IplImage* reimg;
CvHaarClassifierCascade* cascade=(CvHaarClassifierCascade*) cvLoad(cascade_name);
CvMemStorage* storage=cvCreateMemStorage(0);
if(!cascade)
{
qDebug()<<"Coulid not load classifier cascade";
}
if(cascade)
{
CvSeq* faces=cvHaarDetectObjects(image,cascade,storage,1.1,1,CV_HAAR_DO_CANNY_PRUNING,cvSize(10,10));
for(int i=0;i<(faces ? faces->total : 0);i++)
{
string s1="im",re,rename,ex=".jpeg";
sprintf(numstr, "%d", k);
re = s1 + numstr;
rename=re+ex;
char *extract1=new char[rename.size()+1];
extract1[rename.size()]=0;
memcpy(extract1,rename.c_str(),rename.size());
strsize=rename.size();
CvRect *r=(CvRect*) cvGetSeqElem(faces,i);
ul.x=r->x;
ul.y=r->y;
w.x=r->width;
h.y=r->height;
lr.x=(r->x + r->width);
lr.y=(r->y + r->height);
cvSetImageROI(image,cvRect(ul.x,ul.y,w.x,h.y));
image1=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
cvCopy(image, image1, NULL);
reimg=resizeImage(image1, 40, 40, true);
saveImage(reimg,extract1);
img_1=cvarrToMat(reimg);//this img_1 contains the detected faces.
cvResetImageROI(image);
cvRectangle(image,ul,lr,CV_RGB(1,255,0),3,8,0);
j++,count++,k++;
}
}
qDebug()<<"Number of images:"<<count<<endl;
cvNamedWindow("output");//creating a window.
cvShowImage("output",image);//showing resized image.
cvWaitKey(0);//wait for user response.
cvReleaseImage(&image);//releasing the memory.
cvDestroyWindow("output");//destroying the window.
}
**And this the push button 'compare' slot**
void third::on_pushButton_5_clicked()
{
}
If I could access those detected faces from 'void third::on_pushButton_5_clicked()' then only I can use it in the push button slot'compare'. Please help me to fix this..
It sounds like a declaration problem... that the image showing the detected face is not declared under the "public", or maybe "global" variable. If so, its simple.
In your header file, third.h,
class third : public third
{
Q_OBJECT
public:
explicit third(QWidget *parent = 0);
~third();
//declare your image holding the detected face here, which see from your .cpp file is img_1
Following which, for your void third::on_pushButton_5_clicked(),
void third::on_pushButton_5_clicked()
{
cvNamedWindow("img_1");//creating a window.
cvShowImage("img_1",img_1);//showing resized image.
cvWaitKey(0);//wait for user response.
cvReleaseImage(&img_1);//releasing the memory.
cvDestroyWindow("img_1");//destroying the window.
}
The code may have complilation error, as I am typing this directly into stackOverflow.
Do let me know if that solves the problem, if so, do considering clicking the tick button under the arrows and upvoting it. If it doesn't work, comment, and I will see how else I can help. Cheers!

QGraphicsRectItem and QGraphicsScene problems at Scene change

what I want to do is the following:
I have a little GUI with a QGraphicsView. In this graphics View I load a picture:
// m_picture is QPixmap
// image is QImage
// m_graphic is QGraphicsScene
// graphicsView is QGraphicsView
m_picture.convertFromImage(image);
m_graphic->addPixmap(m_picture);
ui->graphicsView->setScene(m_graphic);
This doesn't cause any problems and I can always load a new image without problems.
Now in addition to just display the pictures I want to give the user the ability to draw a rectangle on them ( to "focus" on a specific area ). Actually the user just types in the coordinates in four text boxes on the GUI ( x,y, width,heigth). After providing the coordinates the User presses a button and the rectangle at the following coordinates shall be displayed.
I accomplished this with this code:
void tesseract_gui::show_preview_rect()
{
int x,y,h,w;
x = ui->numBox_x->value();
y = ui->numBox_y->value();
h = ui->numBox_h->value();
w = ui->numBox_w->value();
if( rect_initialized )
{
m_graphic->removeItem(m_rect);
}
else
{
rect_initialized = true;
}
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->addItem(m_rect);
return;
}
The remove call is because I always want to display just one rectangle.
Now as I mentioned this works fine. But if the user now loads another picture ( with the calls at the top of my post ) the program crashes when I try to draw a new rectangle. I
get a Segmentation fault at the call of
m_rect->setPen(QPen(Qt::red));
If I call
m_graphic->removeItem(m_rect);
after loading a new picture I get
QGraphicsScene::removeItem: item 0x8c04080's scene (0x0) is different from this scene (0x8c0a8b0)
and then it crashes with the same Error at setPen.
What I don't get is, I don't change the scene. I just add another picture to it ( or overwrite it) .
Well any suggestions how I could do this right?
Best Regards
// edit:
I tried to do it just with everytime a new rectangle like this:
void tesseract_gui::show_preview_rect()
{
int x,y,h,w;
x = ui->numBox_x->value();
y = ui->numBox_y->value();
h = ui->numBox_h->value();
w = ui->numBox_w->value();
m_graphic->clear();
m_graphic->addRect(x,y,h,w);
return;
}
Problem at this is that with the clear() call it also clears the picture itself from my GraphicsView... so no solution there
// edit:
As suggested I got rid of the Warning like this:
if( m_rect->scene() != 0 )
{
m_graphic->removeItem(m_rect);
}
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->addItem(m_rect);
I know it's not the best way but I tried it also this way ( did not work for me ):
I added the item in the constructor:
m_graphic->addItem(m_rect);
and then
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->update();
and I get the "same" error as always ( Program crashes at m_rect->setPen() )
So it seems the problem always occurs when I already added the rectangle to the graphic, THEN changed the image of m_graphic and then did any operation with m_rect. ( Actually I guess m_graphic takes ownership of m_rect and so this causes the segmentation fault ... ? )
The message QGraphicsScene::removeItem: item 0x8c04080's scene (0x0) is different from this scene (0x8c0a8b0) tells you m_rect is not in any scene at the time you call it. It's probably removed somewhere else in your code or you have 2 variables with the same name in the class hierarchy.
Also, you don't need to remove it from scene to change it. Just change it while it's in the scene. It will get repainted with the new color and geometry in the next paint event.
Even if you REALLY want to remove it before changing it, just check if it's in a scene by calling QGraphicsItem::scene(). There's no need for the init check variable.