Why is getWindowImageRect in OpenCV for Windows does not return the expected value? - opencv

I was trying to make a fullscreen window in OpenCV and the functions provided in OpenCV are working except there is a white edge in the left border and top border. After spending time in investigating the code in OpenCV, I found out that the function getWindowImageRect is somehow returning a value that is 1px more in x, y and 1px less in width and height than the function that I have wrote.
Okay, and here is the question. Why are the two functions returning different values?
Here is the code I have found in OpenCV:
CvRect cvGetWindowRect_W32(const char* name)
{
CvRect result = cvRect(-1, -1, -1, -1);
CV_FUNCNAME( "cvGetWindowRect_W32" );
__BEGIN__;
CvWindow* window;
if (!name)
CV_ERROR( CV_StsNullPtr, "NULL name string" );
window = icvFindWindowByName( name );
if (!window)
EXIT; // keep silence here
RECT rect;
GetClientRect(window->hwnd, &rect);
{
POINT pt = {rect.left, rect.top};
ClientToScreen(window->hwnd, &pt);
result = cvRect(pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top);
}
__END__;
return result;
}
This is the code that I have wrote in Python, which I thought will give me the same result:
def getWindowRect(name)
hwnd = win32gui.FindWindow(None, name)
rect_l, rect_t, rect_r, rect_b = win32gui.GetClientRect(hwnd)
pt_x, pt_y = win32gui.ClientToScreen(hwnd, (rect_l, rect_t))
result = (pt_x, pt_y, rect_r - rect_l, rect_b - rect_t)
return result
The result of using the two above functions give me output like this:
(1, 1, 2559, 1439)
(0, 0, 2560, 1440)
So I wonder if any one can tell me why is this happening?
Note that the test is performed under Windows 10.

Related

how do I fit my mat image to MFC Picture Control Size?

This is the part where the image set to the picture controller:
void CMFCApplication3View::drawImg1(Mat img) {
int nX = img.rows;
int nY = img.cols;
CImage image;
Mat2CImage(&img, image);
CRect rect;//픽쳐 컨트롤의 크기를 저장할 CRect 객체
pictureControl.GetWindowRect(rect);//GetWindowRect를 사용해서 픽쳐 컨트롤의 크기를 받는다.
CDC* dc; //픽쳐 컨트롤의 DC를 가져올 CDC 포인터
dc = pictureControl.GetDC(); //픽쳐 컨트롤의 DC를 얻는다.
image.StretchBlt(dc->m_hDC, 0, 0, nX, nY, SRCCOPY);//이미지를 픽쳐 컨트롤 크기로 조정 rect.Width(), rect.Height()
ReleaseDC(dc);//DC 해제
}
void CMFCApplication3View::drawImg2(Mat img) {
int nX = img.rows;
int nY = img.cols;
CImage image;
Mat2CImage(&img, image);
CRect rect;//픽쳐 컨트롤의 크기를 저장할 CRect 객체
pictureControl2.GetWindowRect(rect);//GetWindowRect를 사용해서 픽쳐 컨트롤의 크기를 받는다.
CDC* dc; //픽쳐 컨트롤의 DC를 가져올 CDC 포인터
dc = pictureControl2.GetDC(); //픽쳐 컨트롤의 DC를 얻는다.
image.StretchBlt(dc->m_hDC, 0, 0, nX, nY, SRCCOPY);//이미지를 픽쳐 컨트롤 크기로 조정 rect.Width(), rect.Height()
ReleaseDC(dc);//DC 해제
}
You don't even attempt to stretch the image to the destination rectangle in this piece of code, you just draw it to a rectangle equal to the image dimensions, which is not a stretch. You get the destination rectangle, but do nothing with it.
You should better implement some "best-fit" algorithm, ie stretch the image to the maximum possible dimensions, occupying either the whole width or the whole height of the destination rectangle, while maintaining its original proportions, ie not distorting it. Here is some code:
CRect rect;
picturecontrol.GetClientRect(rect);
int nImgW = img.cols, nImgH = img.rows; // Original image dimensions
int nCtlW = rect.Width(), nCtlH = rect.Height(); // Destination control dimensions
int nW, nH; // Stretched (draw) dimensions
if (nImgW*nCtlH > nCtlW*nImgH)
{ // Image is proportionately wider than the control, fit it horizontally
nW = nCtlW;
nH = MulDiv(nW, nImgH, nImgW);
}
else
{ // Image is proportionately taller than the control, fit it vertically
nH = nCtlH;
nW = MulDiv(nH, nImgW, nImgH);
}
image.StretchBlt(dc->m_hDC, 0, 0, nW, nH, SRCCOPY);
To determine whether the image is proportionately wider or taller than the destination rectangle the condition originally was of course nImgW/nImgH > nCtlW/nCtlH, but was changed to what is shown in the code above, so as to avoid possible inaccuracies due to integer division (it returns the integer quotient, rather than a rounded value, and has a smaller range too).
A side note, normally you should draw your images in response to the WM_PAINT message (in the OnDraw(), OnPaint() or the controls' OnPaint() events), using the CDC* passed to the function or calling CPaintDC. These call BeginPaint()/EndPaint() instead of GetDC()/ReleaseDC() - you must invalidate the control to trigger a repaint. For a static picture control you can call ctl.SetBitmap() (this in any other point in your code, not while processing WM_PAINT).

Draw permanently on PGraphics (Processing)

I would like to create a brush for drawing on a PGraphics element with Processing. I would like past brush strokes to be visible. However, since the PGraphics element is loaded every frame, previous brush strokes disappear immediatly.
My idea was then to create PGraphics pg in setup(), make a copy of it in void(), alter the original graphic pg and update the copy at every frame. This produces a NullPointerException, most likely because pg is defined locally in setup().
This is what I have got so far:
PGraphics pg;
PFont font;
void setup (){
font = createFont("Pano Bold Kopie.otf", 600);
size(800, 800, P2D);
pg = createGraphics(800, 800, P2D);
pg.beginDraw();
pg.background(0);
pg.fill(255);
pg.textFont(font);
pg.textSize(400);
pg.pushMatrix();
pg.translate(width/2, height/2-140);
pg.textAlign(CENTER, CENTER);
pg.text("a", 0 , 0);
pg.popMatrix();
pg.endDraw();
}
void draw () {
copy(pg, 0, 0, width, height, 0, 0, width, height);
loop();
int c;
loadPixels();
for (int x=0; x<width; x++) {
for (int y=0; y<height; y++) {
pg.pixels[mouseX+mouseY*width]=0;
}
}
updatePixels();
}
My last idea, which I have not attempted to implement yet, is to append pixels which have been touched by the mouse to a list and to draw from this list each frame. But this seems quite complicated to me as it might result into super long arrays needing to be processed on top of the original image. So, I hope there is another way around!
EDIT: My goal is to create a smudge brush, hence a brush which kind of copies areas from one part of the image to other parts.
There's no need to manually copy pixels like that. The PGraphics class extends PImage, which means you can simply render it with image(pg,0,0); for example.
The other thing you could do is an old trick to fade the background: instead of clearing pixels completely you can render a sketch size slightly opaque rectangle with no stroke.
Here's a quick proof of concept based on your code:
PFont font;
PGraphics pg;
void setup (){
//font = createFont("Pano Bold Kopie.otf", 600);
font = createFont("Verdana",600);
size(800, 800, P2D);
// clear main background once
background(0);
// prep fading background
noStroke();
// black fill with 10/255 transparnecy
fill(0,10);
pg = createGraphics(800, 800, P2D);
pg.beginDraw();
// leave the PGraphics instance transparent
//pg.background(0);
pg.fill(255);
pg.textFont(font);
pg.textSize(400);
pg.pushMatrix();
pg.translate(width/2, height/2-140);
pg.textAlign(CENTER, CENTER);
pg.text("a", 0 , 0);
pg.popMatrix();
pg.endDraw();
}
void draw () {
// test with mouse pressed
if(mousePressed){
// slowly fade/clear the background by drawing a slightly opaque rectangle
rect(0,0,width,height);
}
// don't clear the background, render the PGraphics layer directly
image(pg, mouseX - pg.width / 2, mouseY - pg.height / 2);
}
If you hold the mouse pressed you can see the fade effect.
(changing transparency to 10 to a higher value with make the fade quicker)
Update To create a smudge brush you can still sample pixels and then manipulate the read colours to some degree. There are many ways to implement a smudge effect based on what you want to achieve visually.
Here's a very rough proof of concept:
PFont font;
PGraphics pg;
int pressX;
int pressY;
void setup (){
//font = createFont("Pano Bold Kopie.otf", 600);
font = createFont("Verdana",600);
size(800, 800, P2D);
// clear main background once
background(0);
// prep fading background
noStroke();
// black fill with 10/255 transparnecy
fill(0,10);
pg = createGraphics(800, 800, JAVA2D);
pg.beginDraw();
// leave the PGraphics instance transparent
//pg.background(0);
pg.fill(255);
pg.noStroke();
pg.textFont(font);
pg.textSize(400);
pg.pushMatrix();
pg.translate(width/2, height/2-140);
pg.textAlign(CENTER, CENTER);
pg.text("a", 0 , 0);
pg.popMatrix();
pg.endDraw();
}
void draw () {
image(pg,0,0);
}
void mousePressed(){
pressX = mouseX;
pressY = mouseY;
}
void mouseDragged(){
// sample the colour where mouse was pressed
color sample = pg.get(pressX,pressY);
// calculate the distance from where the "smudge" started to where it is
float distance = dist(pressX,pressY,mouseX,mouseY);
// map this distance to transparency so the further the distance the less smudge (e.g. short distance, high alpha, large distnace, small alpha)
float alpha = map(distance,0,30,255,0);
// map distance to "brush size"
float size = map(distance,0,30,30,0);
// extract r,g,b values
float r = red(sample);
float g = green(sample);
float b = blue(sample);
// set new r,g,b,a values
pg.beginDraw();
pg.fill(r,g,b,alpha);
pg.ellipse(mouseX,mouseY,size,size);
pg.endDraw();
}
As the comments mention, one idea is to sample colour on press then use the sample colour and fade it as your drag away from the source area. This shows simply reading a single pixel. You may want to experiment with sampling/reading more pixels (e.g. a rectangle or ellipse).
Additionally, the code above isn't optimised.
A few things could be sped up a bit, like reading pixels, extracting colours, calculating distance, etc.
For example:
void mouseDragged(){
// sample the colour where mouse was pressed
color sample = pg.pixels[pressX + (pressY * pg.width)];
// calculate the distance from where the "smudge" started to where it is (can use manual distance squared if this is too slow)
float distance = dist(pressX,pressY,mouseX,mouseY);
// map this distance to transparency so the further the distance the less smudge (e.g. short distance, high alpha, large distnace, small alpha)
float alpha = map(distance,0,30,255,0);
// map distance to "brush size"
float size = map(distance,0,30,30,0);
// extract r,g,b values
int r = (sample >> 16) & 0xFF; // Like red(), but faster
int g = (sample >> 8) & 0xFF;
int b = sample & 0xFF;
// set new r,g,b,a values
pg.beginDraw();
pg.fill(r,g,b,alpha);
pg.ellipse(mouseX,mouseY,size,size);
pg.endDraw();
}
The idea is to start simple with clear, readable code and only at the end, if needed look into optimisations.

How to stroke a text in dart using canvas 2D context?

I'm currently making a 2D game engine, and I'm working in the messages for the characters, for this I'm using the fillText method of 2DContext however I have no idea how to stroke a text, I have already tried this way:
context.lineWidth = 1;
context.strokeStyle = '#000';
context.fillText(line, x, y);
context.stroke();
But it doesnt work, I already modified the text color and style like so:
context.font = '18pt Arial';
context.fillStyle = "black";
But no clue about the stroke.
When working with the Canvas, the cascade operator .. can help make the code more readable:
context..lineWidth = 3
..strokeStyle = "black"
..strokeText(line, x, y)
..fillStyle = "white"
..fillText(line, x, y);
Found it! the correct way of doing it is:
context.lineWidth = 3;
context.strokeStyle = "black";
context.strokeText(line, x, y);
context.fillStyle = "white";
context.fillText(line, x, y);
Don't forget the fillText after the stroke otherwise what you'll see is a very bold version of the text in the color of the stroke.

Using cvGet2D OpenCV function

I'm trying to get information from an image using the function cvGet2D in OpenCV.
I created an array of 10 IplImage pointers:
IplImage *imageArray[10];
and I'm saving 10 images from my webcam:
imageArray[numPicture] = cvQueryFrame(capture);
when I call the function:
info = cvGet2D(imageArray[0], 250, 100);
where info:
CvScalar info;
I got the error:
OpenCV Error: Bad argument (unrecognized or unsupported array type) in cvPtr2D, file /build/buildd/opencv-2.1.0/src/cxcore/cxarray.cpp, line 1824
terminate called after throwing an instance of 'cv::Exception'
what(): /build/buildd/opencv-2.1.0/src/cxcore/cxarray.cpp:1824: error: (-5) unrecognized or unsupported array type in function cvPtr2D
If I use the function cvLoadImage to initialize an IplImage pointer and then I pass it to the cvGet2D function, the code works properly:
IplImage* imagen = cvLoadImage("test0.jpg");
info = cvGet2D(imagen, 250, 100);
however, I want to use the information already stored in my array.
Do you know how can I solve it?
Even though its a very late response, but I guess someone might be still searching for the solution with CvGet2D. Here it is.
For CvGet2D, we need to pass the arguments in the order of Y first and then X.
Example:
CvScalar s = cvGet2D(img, Y, X);
Its not mentioned anywhere in the documentation, but you find it only inside core.h/ core_c.h. Try to go to the declaration of CvGet2D(), and above the function prototypes, there are few comments that explain this.
Yeah the message is correct.
If you want to store a pixel value you need to do something like this.
int value = 0;
value = ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels +0];
cout << "pixel value for Blue Channel and (i,j) coordinates: " << value << endl;
Summarizing, to plot or store data you must create an integer value (pixel value varies between 0 and 255). But if you only want to test pixel value (like in an if closure or something similar) you can access directly to pixel value without using an integer value.
I think thats a little bit weird when you start but when you work with it 2 o 3 times you will work without difficulties.
Sorry, cvGet2D is not the best way to obtain pixel value. I know its the shortest and clear way because you in only one line of code and knowing coordinates obtain the pixel value.
I suggest you this option. When you see this code you you wiil think that is so complicated but is more effecient.
int main()
{
// Acquire the image (I'm reading it from a file);
IplImage* img = cvLoadImage("image.bmp",1);
int i,j,k;
// Variables to store image properties
int height,width,step,channels;
uchar *data;
// Variables to store the number of white pixels and a flag
int WhiteCount,bWhite;
// Acquire image unfo
height = img->height;
width = img->width;
step = img->widthStep;
channels = img->nChannels;
data = (uchar *)img->imageData;
// Begin
WhiteCount = 0;
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{ // Go through each channel of the image (R,G, and B) to see if it's equal to 255
bWhite = 0;
for(k=0;k<channels;k++)
{ // This checks if the pixel's kth channel is 255 - it can be faster.
if (data[i*step+j*channels+k]==255) bWhite = 1;
else
{
bWhite = 0;
break;
}
}
if(bWhite == 1) WhiteCount++;
}
}
printf("Percentage: %f%%",100.0*WhiteCount/(height*width));
return 0;
This code count white pixels and gives you a percetage of white pixels in the image.

mouse handler in opencv for large images, wrong x,y coordinates?

i am using images that are 2048 x 500 and when I use cvShowImage, I only see half the image. This is not a big deal because the interesting part is on the top half of the image. Now, when I use the mouseHandler to get the x,y coordinates of my clicks, I noticed that the coordinate for y (the dimension that doesnt fit in the screen) is wrong.
It seems OpenCV think this is the whole image and recalibrates the coordinate system although we are only effectively showing half the image.
I would need to know how to do 2 things:
- display a resized image that would fit in the screen
get the proper coordinate.
Did anybody encounter similar problems?
Thanks!
Update: it seems the y coordinate is divided by 2 of what it is supposed to be
code:
EXPORT void click_rect(uchar * the_img, int size_x, int size_y, int * points)
{
CvSize size;
size.height = size_y ;
size.width = size_x;
IplImage * img;
img = cvCreateImageHeader(size, IPL_DEPTH_8U, 1);
img->imageData = (char *)the_img;
img->imageDataOrigin = img->imageData;
img1 = cvCreateImage(cvSize((int)((size.width)) , (int)((size.height)) ),IPL_DEPTH_8U, 1);
cvNamedWindow("mainWin",CV_WINDOW_AUTOSIZE);
cvMoveWindow("mainWin", 100, 100);
cvSetMouseCallback( "mainWin", mouseHandler_rect, NULL );
cvShowImage("mainWin", img1 );
//// wait for a key
cvWaitKey(0);
points[0] = x_1;
points[1] = x_2;
points[2] = y_1;
points[3] = y_2;
//// release the image
cvDestroyWindow("mainWin");
cvReleaseImage(&img1 );
cvReleaseImage(&img);
}
You should create a window with the CV_WINDOW_KEEPRATIO flag instead of the CV_WINDOW_AUTOSIZE flag. This temporarily fixes the problem with your y values being wrong.
I use OpenCV2.1 and visual studio C++ compiler. I fix this problem with another flag CV_WINDOW_NORMAL and work properly and returns correct coordinates, this flag enables you to resize the image window.
cvNamedWindow("Box Example", CV_WINDOW_NORMAL);
I am having the same problem with OpenCV 2.1 using it with Windows and mingw compiler. It took me forever to find out what was wrong. As you describe it, cvSetMouseCallback gets too large y coordinates. This is apparently due to the image and the cvNamedWindow it is shown in being bigger than my screen resolution; thus I cannot see the bottom of the image.
As a solution I resize the images to a fixed size, such that they fit on the screen (in this case with resolution 800x600, which can be any other values:
// g_input_image, g_output_image and g_resized_image are global IplImage* pointers.
int img_w = cvGetSize(g_input_image).width;
int img_h = cvGetSize(g_input_image).height;
// If the height/width ratio is greater than 6/8 resize height to 600.
if (img_h > (img_w*6)/8) {
g_resized_image = cvCreateImage(cvSize((img_w*600)/img_h, 600), 8, 3);
}
// else adjust width to 800.
else {
g_resized_image = cvCreateImage(cvSize(800, (img_h*800)/img_w), 8, 3);
}
cvResize(g_output_image, g_resized_image);
Not a perfect solution, but works for me...
Cheers,
Linus
How are you building the window? You are not passing CV_WINDOW_AUTOSIZE to cvNamedWindow(), are you?
Share some source, #Denis.

Resources