OpenCv 2.3 C - How to isolate object inside image
One way to do it is to simply execute the bounding box technique to detect the digit, as illustrated by the image below:
Since your image is already processed the bounding box technique I use is a lot simpler.
After that procedure, all you really need to do is set the ROI (Region of Interest) of the original image to the area defined by the box to achieve the crop effect and isolate the object:
Notice that in the resulting image there is one extra row/column of pixels in the border that are not white. Well, they are not black either. That's because I didn't performed any threshold method to binarize the image to black and white. The code below demonstrates the bounding box technique being executed on a grayscale version of the image.
This is pretty much the roadmap to achieve what you want. For educational purposes I'm sharing the code I wrote using the C++ interface of OpenCV. I'm sure you are capable of converting it to the C interface.
#include <cv.h>
#include <highgui.h>
#include <vector>
int main(int argc, char* argv[])
{
cv::Mat img = cv::imread(argv[1]);
// Convert RGB Mat to GRAY
cv::Mat gray;
cv::cvtColor(img, gray, CV_BGR2GRAY);
// Store the set of points in the image before assembling the bounding box
std::vector<cv::Point> points;
cv::Mat_<uchar>::iterator it = gray.begin<uchar>();
cv::Mat_<uchar>::iterator end = gray.end<uchar>();
for (; it != end; ++it)
{
if (*it) points.push_back(it.pos());
}
// Compute minimal bounding box
cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));
// Draw bounding box in the original image (debug purposes)
//cv::Point2f vertices[4];
//box.points(vertices);
//for (int i = 0; i < 4; ++i)
//{
//cv::line(img, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
//}
//cv::imshow("box", img);
//cv::imwrite("box.png", img);
// Set Region of Interest to the area defined by the box
cv::Rect roi;
roi.x = box.center.x - (box.size.width / 2);
roi.y = box.center.y - (box.size.height / 2);
roi.width = box.size.width;
roi.height = box.size.height;
// Crop the original image to the defined ROI
cv::Mat crop = img(roi);
cv::imshow("crop", crop);
cv::imwrite("cropped.png", crop);
cvWaitKey(0);
return 0;
}
Jayyrus
I'm an Italian Software Engineer. I like very much to learn and study new technologies
Updated on October 11, 2020Comments
-
Jayyrus over 3 years
i have an image like:
i want to remove the black rows and cols round the number. So i want that the result is:
i try this:
void findX(IplImage* imgSrc,int* min, int* max){ int i; int minFound=0; CvMat data; CvScalar maxVal=cvRealScalar(imgSrc->width * 255); CvScalar val=cvRealScalar(0); //For each col sum, if sum < width*255 then we find the min //then continue to end to search the max, if sum< width*255 then is new max for (i=0; i< imgSrc->width; i++){ cvGetCol(imgSrc, &data, i); val= cvSum(&data); if(val.val[0] < maxVal.val[0]){ *max= i; if(!minFound){ *min= i; minFound= 1; } } } } void findY(IplImage* imgSrc,int* min, int* max){ int i; int minFound=0; CvMat data; CvScalar maxVal=cvRealScalar(imgSrc->width * 255); CvScalar val=cvRealScalar(0); //For each col sum, if sum < width*255 then we find the min //then continue to end to search the max, if sum< width*255 then is new max for (i=0; i< imgSrc->height; i++){ cvGetRow(imgSrc, &data, i); val= cvSum(&data); if(val.val[0] < maxVal.val[0]){ *max=i; if(!minFound){ *min= i; minFound= 1; } } } } CvRect findBB(IplImage* imgSrc){ CvRect aux; int xmin, xmax, ymin, ymax; xmin=xmax=ymin=ymax=0; findX(imgSrc, &xmin, &xmax); findY(imgSrc, &ymin, &ymax); aux=cvRect(xmin, ymin, xmax-xmin, ymax-ymin); //printf("BB: %d,%d - %d,%d\n", aux.x, aux.y, aux.width, aux.height); return aux; }
So i use:
IplImage *my_image = cvLoad.... CvRect bb = findBB(my_image); IplImage *new_image = cvCreateImage(cvSize(bb.width,bb.height), my_image->depth, 1); cvShowImage("test",new_image);
it doesn't work good, cause i try to check if in new image there are black rows or cols and they are present. what can i do? can someone help me? (sorry for my english!)
-
Jayyrus about 12 yearshow can i convert std::vector<cv::Point> points; cv::Mat_<uchar>::iterator it = gray.begin<uchar>(); cv::Mat_<uchar>::iterator end = gray.end<uchar>(); for (; it != end; ++it) { if (*it) points.push_back(it.pos()); } // Compute minimal bounding box cv::RotatedRect box = cv::minAreaRect(cv::Mat(points)); into c interface?
-
karlphillip about 12 yearsIt's best to understand what each of these does: the first part simply adds the coordinates of pixels that are not black to a vector of points. About the second part,
cv::RotatedRect
replaces the oldCvBox2D
(which is what you need to use). -
Jayyrus about 12 yearshow can i know the number of pixel that are not black, using that number to allocate a vector?
-
karlphillip about 12 yearsYou are starting to hijack your own thread. And remember, I'm not the OpenCV bible. Let's keep one question per thread, ok? If you have more questions feel free to ask them in new threads. I'll give you a tip this time: check
cvCountNonZero()
-
karlphillip about 12 yearsIf you are having so much trouble converting it to the C interface, you could easilly write a wrapper around my code. I suspect you are already using a C++ compiler anyway.
IplImage* crop_digit(IplImage* input)
. It's very easy to convertcv::Mat
toIplImage
and there are several threads in Stackoverflow that show how to do this. -
Mzk almost 12 years@Karlphillip. How about the performance using the wrapper instead of converting it to C?
-
karlphillip almost 12 yearsI haven't seen anyone complaining about performance loss using the C++ API. Not all C++ methods are wrappers. The C++ interface provides a lot of extra stuff that was carefully implemented aiming for optimization. The only cases were I wouldn't use the C++ API is on projects that I don't have access to a C++ compiler.
-
Valentin H over 10 yearsInteresting! I've always used contour for that. This technique is much better, as you don't have to decide which contour(s) to take.
-
Qadir Hussain about 9 years@karlphillip can you please translate this code in Java (Android) using OpenCV.
-
karlphillip about 9 years@QadirHussain haha not a chance, but kudos for asking.
-
Hans Malherbe about 8 yearsIf box.angle turns out to be -90, box width and height are swapped causing an assertion in img(roi)
-
Aqeel iqbal almost 7 yearsTo anyone who is having rotated rectangle like me. it is giving 90 degree rotated cropped image. so thats what i did.
cv::Rect roi = box.boundingRect();
and comment outcv::Rect roi;
roi.x = box.center.x - (box.size.width / 2);
roi.y = box.center.y - (box.size.height / 2);
roi.width = box.size.width;
roi.height = box.size.height;