OpenCv 2.3 C - How to isolate object inside image

20,439

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;
}
Share:
20,439
Jayyrus
Author by

Jayyrus

I'm an Italian Software Engineer. I like very much to learn and study new technologies

Updated on October 11, 2020

Comments

  • Jayyrus
    Jayyrus over 3 years

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

  • Jayyrus
    Jayyrus about 12 years
    how 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
    karlphillip about 12 years
    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).
  • Jayyrus
    Jayyrus about 12 years
    how can i know the number of pixel that are not black, using that number to allocate a vector?
  • karlphillip
    karlphillip about 12 years
    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()
  • karlphillip
    karlphillip about 12 years
    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.
  • Mzk
    Mzk almost 12 years
    @Karlphillip. How about the performance using the wrapper instead of converting it to C?
  • karlphillip
    karlphillip almost 12 years
    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.
  • Valentin H
    Valentin H over 10 years
    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.
  • Qadir Hussain
    Qadir Hussain about 9 years
    @karlphillip can you please translate this code in Java (Android) using OpenCV.
  • karlphillip
    karlphillip about 9 years
    @QadirHussain haha not a chance, but kudos for asking.
  • Hans Malherbe
    Hans Malherbe about 8 years
    If box.angle turns out to be -90, box width and height are swapped causing an assertion in img(roi)
  • Aqeel iqbal
    Aqeel iqbal almost 7 years
    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;