1

I have tried findNonZero and with the boundingRect. But nothing is helping me. I am new to C++ OpenCV. I did this using Python OpenCV which involved NumPy, but unfortunately I am not able to do the same in C++.

Input image

enter image description here

Python:

def crop_with_arg(refactor_image):
  mask = refactor_image > 0
  coord = np.argwhere(mask)
  x0, y0 = coord.min(axis=0)
  x1, y1 = coord.max(axis=0) + 1
  cropped = refactor_image[x0:x1, y0:y1]
  return cropped

 def crop_image(image_crop, tol=50):
    mask = image_crop > tol
    return image_crop[np.ix_(mask.any(1), mask.any(0))]

 compressed_img = crop_with_arg(gray) 
 croppped_image = crop_image(compressed_img, tol=50)

I am writing the code in Objective C++ to have wrapper for iOS.

Praveen
  • 267
  • 1
  • 5
  • 19
  • 1
    You should post some code like your Python code which works and your C++ one which does not work. – coincoin Nov 12 '19 at 14:05
  • 3
    What does not work? In what way is the output incorrect? What debugging have you done so far? Have you seen the `if` actually being entered at some point in your debugger or your debugging prints? Is `nonBlackList` correctly filled after the loops? Are the bounds of `bb` correct? If not, what are they and what should they be? – Aziuth Nov 12 '19 at 14:33
  • 2
    >What does not work? The cropping is not working. > In what way is the output incorrect? I am getting the same image with no cropping being done >What debugging have you done so far? I have tried to understand data and see if the looping is working or not. The x and y is filled. > The bounds of bb have no x and y value. and hence the cropping is not working. This C++ code is not working in my scenario. Hence was asking if i am on the right path – Praveen Nov 12 '19 at 14:52
  • What do you mean by "the y value is not filled"? The points created having no y-value? One thing I see that *might* be the issue is that your x-y-order in `graycloned.at(j,i)` is different than in `cv::Point(i,j)` ( (j,i) vs (i,j) ). If i is the column, should it be `graycloned.at(i,j)` instead? – Aziuth Nov 12 '19 at 15:01
  • 1
    that was a rookie mistake! I did correct it. Now I am getting these values. Printing description of bb: (cv::Rect) bb = (x = -390252292, y = 1, width = 72760040, height = 1) – Praveen Nov 12 '19 at 15:28
  • @Aziuth That's because `cv::at` is in [row, column order](https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html#a5db6b18954e334048cb35752f49c6f8c) while `cv::Point` is in [column, row order](https://docs.opencv.org/3.4/db/d4e/classcv_1_1Point__.html#a8db927bb4ec0aa348367566cb6219c0f). (Yes, this gets confused all the time.) – beaker Nov 12 '19 at 17:31
  • @Praveen I am unable to reproduce your error. I get a bounding box of `[386 x 56 from (166, 497)]`. – beaker Nov 12 '19 at 17:33
  • That's interesting, did you use my code or you corrected something! – Praveen Nov 12 '19 at 17:37
  • All I did was load your image and run your code (plus `cout` and a couple of `imshow`). – beaker Nov 12 '19 at 17:38
  • Yes, this is working. I had removed grayThres = gray > 50; line while I was debugging. Unfortunately, imshow doesn't working while writing wrappers and header file. Thank you! – Praveen Nov 13 '19 at 08:12

2 Answers2

0

For a grayscale image, the following code works perfectly.

The value 50 is the threshold limit that can be set based on the preprocessing that is done.

grayThres = gray > 50;
graycloned = grayThres.clone();
std::vector<cv::Point> nonBlackList;
nonBlackList.reserve(graycloned.rows*graycloned.cols);

for(int j=0; j<graycloned.rows; ++j)
    for(int i=0; i<graycloned.cols; ++i)
    {
        // if not black: add to the list
        if(graycloned.at<cv::Vec2b>(j,i) != cv::Vec2b(0,0))
        {
            nonBlackList.push_back(cv::Point(j,i));
        }
    }
// create bounding rect around those points
cv::Rect bb = cv::boundingRect(nonBlackList);
cv:: Mat returnImage = gray(bb);
Praveen
  • 267
  • 1
  • 5
  • 19
0

I think something like this, using cv::boundingRect(), will be pretty efficient:

#include <iostream>
#include <opencv2/opencv.hpp>

int
main(int argc,char*argv[])
{
    // Load image as greyscale
    cv::Mat im = cv::imread("thing.jpg", cv::IMREAD_GRAYSCALE);

    // Threshold image at 128
    cv::Mat thresh;
    cv::threshold(im, thresh, 128, 255, cv::THRESH_BINARY);

    // Do the actual work
    double t = (double)cv::getTickCount();
    cv::Rect ROI = cv::boundingRect(thresh);
    t = ((double)cv::getTickCount() - t)/cv::getTickFrequency(); 

    // Print timing and results
    std::cout << "Time: " << t*1000.0 << "ms" << std::endl;
    std::cout << ROI << std::endl;
}

Sample Output

Time: 0.317279ms
[253 x 48 from (113, 503)]

By the way, you can do this much more simply from the command-line with ImageMagick which is included in most Linux distros and is available for macOS and Linux:

# Print coordinates of trim-box
convert thing.jpg -threshold 50% -format %@ info:
253x48+113+503

Or, actually do the trim:

convert thing.jpg -threshold 50% -trim result.jpg

enter image description here

Keywords: Image processing, OpenCV, C++, trim, trim box, trim-box, crop, crop-box, border, bordering, remove border, trim border, ROI, boundingRect(), cv::boundingRect()

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432