Execution breaks at C++ opencv copyTo() function - c++

My goal is to pad my segmented image with zeros along the border as I needed to close it (for filling in small holes in my foreground). Here tmp is an CV_8UC3 segmented image Mat obtained from my image frame, in which all my background pixels have been blacked out. I have manually created a binary mask from tmp and stored it in Mat bM, which is the same size and type as my image Mat frame.
Mat bM = Mat::zeros(frame.rows, frame.cols, CV_8UC1);
for(i=0;i<frame.rows;i++)
{
for(j=0;j<frame.cols;j++)
{
if(tmp.at<Vec3b>(i,j)[0] != 0 && tmp.at<Vec3b>(i,j)[1] != 0 && tmp.at<Vec3b>(i,j)[0] != 0)
bM.at<uchar>(i,j) = 255;
}
}
Mat padded;
int padding = 6;
padded.create(bM.rows + 2*padding, bM.cols + 2*padding, bM.type());
padded.setTo(Scalar::all(0));
bM.copyTo(padded(Rect(padding, padding, bM.rows, bM.cols)));
My execution breaks at the last line in Visual Studio giving the following error:
Assertion failed <0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0<=roi.height && roi.y+roi.height <=m.rows>
While I understand what that means, I cant figure out why it would throw this error as my source image is within bounds of my target image. I've stepped through my code and am sure it breaks at that specific line. From what I've read, the cv::Rect constructor can be given offsets the way I've passed the offsets padding and padding, and these offsets are taken from the top left corner of the image. Can the copyTo function be used this way? Or is my error elsewhere?

The CV::Rect constructor is different from the CV::Mat constructor.
Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);
The cv::Rect parameters are offset in x, offset in y and then width first and height at last.
So when you do this:
bM.copyTo(padded(Rect(padding, padding, bM.rows, bM.cols)));
You create a cv::Rect with width bM.rows and heigth bM.cols. Which is the oposite of what you need.
Change it to:
bM.copyTo(padded(Rect(padding, padding, bM.cols, bM.rows)));

Related

I'm getting an error from the implementation of my region of interest

Error message:
(-215) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols &&
0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in
function cv::Mat::Mat
This is my code
Rect eye_rec(200, 300, 168, 168);
Point hand_pos(100, 100);
Mat des, mask = (cv::Mat::zeros(hand.size(), CV_8UC1));
mask(eye_rec).setTo(255);
seamlessClone(eye,hand, mask,hand_pos,des,NORMAL_CLONE);
imshow("clone", des);
waitKey(0);
i cant really understand the error message though..
Your error code generally means, that ROI you want to crop is out of the bounds of the source matrix - e.g. source matrix is of size 480x480 and you want to crop out ROI of size 300x300 from position (200, 200), where 300+200 > 480.
According to docs
src – Input 8-bit 3-channel image.
dst – Input 8-bit 3-channel image.
mask – Input 8-bit 1 or 3-channel image.
result – Output image with the same size and type as dst.
src, dst and result should be of type CV_8UC3 - three channel images, while you are passing just one channel images CV_8UC1, which most likely cause the error here.
The solution is to use 3-channel (color) images or different operation accepting 1-channel images.
hand.convertTo(hand, CV_8UC3);
eye.convertTo(eye, CV_8UC3);
Point hand_pos(hand.cols/2,hand.rows/2); //this code should put the eye image in the middle of the hand image
Mat des, mask = (cv::Mat::zeros(eye.size(), CV_8UC3));
des.convertTo(des, CV_8UC3);
mask = 255 * Mat::ones(eye.rows, eye.cols, eye.depth()); // creating a mask of all white from the eye image
seamlessClone(eye,hand, mask,hand_pos,des,NORMAL_CLONE);
imshow("normalclone", des); waitKey(0);
seamlessClone(eye,hand,mask,hand_pos,des, MIXED_CLONE);
imshow("mixclone",des); waitKey(0)
waitKey(0);
This change helped me, hope it helps others too, thanks #Filip Kočica

Scaling opencv matrix of type CV_8UC1 gives zeroes and ones

I am attempting to scale a grayscale image of type 8UC1 by 1.0f/255,
by the following operation
image.convertTo(image,CV_32F,1.0f/255,0); //convert and scale
After inspecting the output of the above, I find that all values are too close to zero. For instance, at a point where the value should be 0.2784, I'm getting 1.23417e-06.
So, I tried to see if I could undo the scaling and get the input back i.e. multiplying the result from above by 255, using
cv::imwrite("undo_scaling.jpg",image*255); //rescale and write to disk
strangely, the input image can be reconstructed.
Where am I going wrong with the scaling operation?
EDIT
The following is my attempt to preprocess an image. That involves the followings steps
Apply a mask
Crop the result
Finally, scale the pixel values by 255
I use the following code:
cv::Mat maskCrop(std::string imageName, std::string maskName)
{
cv::Mat image,mask,final_image;
image = cv::imread( imageName, CV_LOAD_IMAGE_GRAYSCALE);
mask = cv::imread( maskName,CV_LOAD_IMAGE_GRAYSCALE);
cv::resize(image, image, mask.size()); // make the size of mask and image same
cv::bitwise_and(image, mask, final_image); //Apply mask
// define rectangular window for cropping
int offset_x = 1250; // top left corner, for cropping
int offset_y = 1550;
cv::Rect roi;
roi.x = offset_x;
roi.y = offset_y;
roi.width = 550;
roi.height = 650;
// Crop the original image to the defined ROI //
cv::Mat crop = dstImage(roi);
crop.convertTo(crop,CV_32F,1.0f/255,0);
return crop;
}
Below is the input image:
The following is the mask to be applied on it:

How to verify if rect is inside cv::Mat in OpenCV?

Is there anything like cv::Mat::contains(cv::Rect) in Opencv?
Background:
After detecting objects as contours and trying to access ROIs by using cv::boundingRect my application crashed. OK, that's because the bounding rects of the object close to image border may be not entirely within the image.
Now I skip the objects not entirely in image by this check:
if(
cellRect.x>0 &&
cellRect.y>0 &&
cellRect.x + cellRect.width < m.cols &&
cellRect.x + cellRect.width < m.rows) ...
where cellRect is the bounding rect of the object and m is the image.
I hope there is a dedicated opencv function for this.
Simple way is to use the AND (i.e. &) operator.
Assume you want to check if cv::Rect rect is inside cv::Mat mat:
bool is_inside = (rect & cv::Rect(0, 0, mat.cols, mat.rows)) == rect;
You can create rect "representing"(x,y = 0, width and height equal to image width and height) your image and check whether it contains bounding rects of your contours. To achieve that you need to use rect intersection - in OpenCV it's very simple, just use rect1 & rect2. Hope that code makes it clear:
cv::Rect imgRect = cv::Rect(cv::Point(0,0), img.size());
cv::Rect objectBoundingRect = ....;
cv::Rect rectsIntersecion = imgRect & objectBoundingRect;
if (rectsIntersecion.area() == 0)
//object is completely outside image
else if (rectsIntersecion.area() == objectBoundingRect.area())
//whole object is inside image
else
//((double)rectsIntersecion.area())/((double)objectBoundingRect.area()) * 100.0 % of object is inside image
Here is a method to judge whether a rectangle contains an other rectangle.
you can get the size info from cv::Mat first, and then use method below:
public bool rectContainsRect(Rectangle containerRect, Rectangle subRect)
{
if( containerRect.Contains(new Point(subRect.Left, subRect.Top))
&& containerRect.Contains(new Point(subRect.Right, subRect.Top))
&& containerRect.Contains(new Point(subRect.Left, subRect.Bottom))
&& containerRect.Contains(new Point(subRect.Right, subRect.Bottom)))
{
return true;
}
return false;
}

Pad an image with zeros

I am coding in C++ and am trying to resize image 2 to the same dimensions as image 1, however I don't want to stretch the image. I am trying to copy image2 to the padded matrix (at the point 0,0). Am getting the error:
OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) in Mat, file C:\opencv\opencv\modules\core\src\matrix.cpp, line 323
Code is below. Thanks in advance
Mat padded;
padded.setTo(cv::Scalar::all(0));
padded.create(image1.rows,image1.cols, image2.type());
image2.copyTo(padded(Rect(0, 0, image2.rows, image2.cols)));
You can use the OpenCV function copyMakeBorder to pad an image:
To achieve what you want, you can try the following:
cv::Mat padded;
//Assuming that dimensions of image1 are larger than that of image2
//Calculate padding amount so that total size after padding is equal to image1's size
int rowPadding = image1.rows - image2.rows;
int colPadding = image1.cols - image2.cols;
cv::copyMakeBorder(image2, padded, 0, rowPadding, 0, colPadding, cv::BORDER_CONSTANT, cv::Scalar::all(0));
It is almost the same as I asked before. As for you exmple, you just swap cols and rows in function call as berak already mentioned above.
Method copyMakeBorder(...) exists both for Mat and oclMat, so it could be usefull if you will try openCL-extension of openCV for better perfomance.
I don't know the syntax, but here's a pseudo-code
float width1, height1; //size of image1
float width2, height2; //original size of image2
float scale;
bool portrait = width2 < height2;
if(portrait) scale = height1 / height2;
else scale = width1 / width2;
float scaled_width2 = width2 * scale;
float scaled_height2 = height2 * scale;
When the image is portrait, we fill height, otherwise we fill the width.
By doing it this way, the image will get to its maximum size without being stretched

OpenCV: Error copying one image to another

I am trying to copy one image to another pixel by pixel (I know there are sophisticated methods available. I am trying to solve another problem and answer to this will be useful).
This is my code:
int main()
{
Mat Img;
Img = imread("../../../stereo_images/left01.jpg");
Mat copyImg = Mat::zeros(Img.size(), CV_8U);
for(int i=0; i<Img.rows; i++){
for(int j=0; j<Img.cols; j++){
copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
}}
namedWindow("Image", CV_WINDOW_AUTOSIZE );
imshow("Image", Img);
namedWindow("copyImage", CV_WINDOW_AUTOSIZE );
imshow("copyImage", copyImg);
waitKey(0);
return 0;
}
When I run this code in visual studio I get the following error
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 < (unsigned)si
ze.p[0] && (unsigned)(i1*DataType<_Tp>::channels) < (unsigned)(size.p[1]*channel
s()) && ((((sizeof(size_t)<<28)|0x8442211) >> ((DataType<_Tp>::depth) & ((1 << 3
) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file c:\opencv\opencv-2.4.9\ope
ncv\build\include\opencv2\core\mat.hpp, line 537
I know for fact that Img's type is CV_8U. Why does this happen ?
Thanks!
// will read in a rgb image , no matter what the content is
Img = imread("../../../stereo_images/left01.jpg");
to make it read grayscale images use:
Img = imread("../../../stereo_images/left01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
then, you don't need to copy per pixel (and you should even avoid that), just use:
Mat im2 = Img.clone();
if you do per-pixel loops, watch out to get the indices right. it's row-col world here, not x,y, so it should be:
copyImg.at<uchar>(i,j) = Img.at<uchar>(i,j);
in your case
I know for fact that Img's type is CV_8U.
But CV_8U is just the image depth (8-bit U-nsigned). The type also specifies the number of channels, which is usually three. One for blue, one for green and one for red in this order as default for OpenCV. The type would be CV_8UC3 (C-hannels = 3). imread will convert even a black and white image to a 3-channel image by default. imread(filename, CV_LOAD_IMAGE_GRAYSCALE) will load a 1-channel image (CV_8UC1). But if you're not sure the easiest solution is
Mat copyImg = Mat::zeros(Img.size(), Img.type());
To access the array elements you have to know the size of it. Using .at<uchar>() on a 3-channel image will only access the first channel because you have 3*8 bit per pixel. So on a 3-channel image you have to use
copyImg.at<Vec3b>(i,j) = Img.at<Vec3b>(i,j);
where Vec3b is a cv::Vec<uchar, 3>. You should also note that the first argument of at<>(,) is the index along dim 0 which are the rows and second argument cols. Or in other words in classic 2d-xy-chart order you access a pixel with .at<>(y,x) == .at<>(Point(x,y)).
your problem is with this line :
copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
It should be :
copyImg.at<uchar>(i,j) = Img.at<uchar>(i,j);
Note that if you want to copy image you can simply do this :
Mat copyImg = Img.clone();