10

i have an image like:

source

i want to remove the black rows and cols round the number. So i want that the result is:

output

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!)

Slothworks
  • 1,083
  • 14
  • 18
Jayyrus
  • 12,961
  • 41
  • 132
  • 214
  • Your image has only one number, so I'm not sure by what you mean with black rows and cols "round" the number. – karlphillip Apr 25 '12 at 12:33
  • sorry for my english again.. for "round" i mean the rows and cols with all value black (0) near the number – Jayyrus Apr 25 '12 at 12:35
  • Humm.. I think you are talking on the pixel level. Now think about it this way, if you invert this image, everything that is black will become white and the number itself would be black. Wouldn't this be better? – karlphillip Apr 25 '12 at 12:39
  • i think i didn't explain good my problem. the only thing i want to do is to create a new image with only the number inside so removing from source image the rows and cols that don't contain the number so white pixel – Jayyrus Apr 25 '12 at 12:41
  • It would be a lot clearer if you modify your input image in MS Paint or some other drawing tool to show us what is the result you are looking for. – karlphillip Apr 25 '12 at 12:44
  • see edit :) ( i resize image of 400% to show better ) – Jayyrus Apr 25 '12 at 12:51
  • Added an answer. Review it carefully. – karlphillip Apr 25 '12 at 14:36
  • Is the second image you need as result? – Abid Rahman K Apr 25 '12 at 14:36

1 Answers1

24

One way to do it is to simply execute the bounding box technique to detect the digit, as illustrated by the image below:

enter image description here

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:

enter image description here

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;
}
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • how can i convert std::vector points; cv::Mat_::iterator it = gray.begin(); cv::Mat_::iterator end = gray.end(); 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? – Jayyrus Apr 25 '12 at 16:54
  • It'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 old `CvBox2D` (which is what you need to use). – karlphillip Apr 25 '12 at 17:00
  • how can i know the number of pixel that are not black, using that number to allocate a vector? – Jayyrus Apr 25 '12 at 20:38
  • 1
    You 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()`](http://opencv.willowgarage.com/documentation/operations_on_arrays.html#countnonzero) – karlphillip Apr 25 '12 at 20:42
  • If 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 convert `cv::Mat` to `IplImage` and there are several threads in Stackoverflow that show how to do this. – karlphillip Apr 25 '12 at 20:45
  • @Karlphillip. How about the performance using the wrapper instead of converting it to C? – Mzk May 15 '12 at 00:45
  • I 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. – karlphillip May 15 '12 at 00:51
  • 2
    Interesting! I've always used contour for that. This technique is much better, as you don't have to decide which contour(s) to take. – Valentin H Oct 31 '13 at 08:18
  • @karlphillip can you please translate this code in Java (Android) using OpenCV. – Qadir Hussain Feb 10 '15 at 10:08
  • 1
    @QadirHussain haha not a chance, but kudos for asking. – karlphillip Feb 10 '15 at 10:38
  • 1
    If box.angle turns out to be -90, box width and height are swapped causing an assertion in img(roi) – Hans Malherbe Feb 14 '16 at 07:25
  • To 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 out `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;` – Aqeel iqbal May 16 '17 at 20:34