F. Bar F. Bar - 15 days ago 8
C++ Question

How to achieve bounding box around image containing text

I'm currently working on a small project using OpenCV which consists of detected text within natural images. I am trying to achieve the outcome of drawing bounding boxes around all text contained within an image.

I've used a number of functions displayed in the code below:

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

using namespace cv;
using namespace std;



Mat src, newsrc, dst, dil;
int threshold_value = 150;
int threshold_type = 3;;
int const max_BINARY_value = 255;

int main ()
{
string imageLocation = "/Users/samtozer/Documents/OpenCV/image4.jpg";
src = imread(imageLocation, 1);

cvtColor(src, newsrc, CV_BGR2GRAY);
threshold( newsrc, dst, threshold_value, max_BINARY_value, CV_THRESH_BINARY );
dilate(dst, dil, Mat(), Point(-1, -1), 7);

Mat out = Mat::zeros(src.rows, src.cols, CV_8UC3);

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;

findContours( dil, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

int idx = 0;

for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( out, contours, idx, color, CV_FILLED, 8, hierarchy );
}

std::vector<cv::Rect> rects;

for(size_t i=0; i<contours.size(); ++i){
rects[i] = cv::boundingRect(contours[i]);
cv::rectangle(src, rects[i], cv::Scalar(0, 0, 255));
}


namedWindow("Original", CV_WINDOW_AUTOSIZE);
imshow("Original", src);
waitKey(0);
return 0;
}


I've firstly converted the image to grayscale then applied binary thresholding.

I've then applied dilation iterated 7 times to thicken the lines.

I've then used the findConours function and achieved the following:

Original Image
Original Image

Output
Outputted Image

My question is, how would I go about drawing a bounding box and apply to the original image. I've searched online for examples but they don't seem to make sense to me.

Also, if anyone has a better/more efficient way to achieve the result, could you please share it with me.

Thanks in advance.

RESULTS
enter image description here
enter image description here

Answer

You can have a look here boundingRect and here rectangle to both find the coordinates of the rectangle around each of your contours, and draw it in a given color.

If you want one single rectangle around all the text zones, you can simply compute its coordinates by looking for y_min, y_max, x_min, x_max in your contours.

std::vector<cv::Rect> rects;
for(size_t i=0; i<contours.size(); ++i){
    rects.push_back(cv::boundingRect(contours[i]));
    cv::rectangle(src, rects[i], cv::Scalar(0, 0, 255));
}
cv::imshow("MyImage", src);

should do the trick.

edit: I now see that you want to have a single box around each word. You should try to diminish your dilation operation, in order to have separate contours for each word. That should be easy to do, as the spaces between letters of a same word are smaller than spaces between words. Then, the pseudocode above should work for each word separately.

Comments