OpenCV Drawn Lines on Contour (c++) - c++

I want to draw lines on the following picture, that I can caclulate the length of each line. My problem is that when I try it with the following code my image get completly white.
std::vector<cv::Vec2f> lines;
cv::HoughLines(drawing_small, lines, 1, CV_PI/180, 50, 0, 0 );
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
cv::Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
cv::line( drawing_small, pt1, pt2, cv::Scalar(0,100,0), 3, CV_AA);
}
Something like that:
I would be very happy if anyone can say me what I can do.
Update
This is what I do before:
cv::findContours(dst, contours_small, hierarchy_small, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
//Detecting Contours
std::vector<cv::Point2f> ContCenter_small(contours_small.size());
cv::Mat drawing_small = cv::Mat::zeros( dst.size(), CV_8UC3 );
for( int i = 0; i < contours_small.size(); i++ )
{
ContArea_small[i] = moments(contours_small[i], false);
ContCenter_small[i] = cv::Point2f(ContArea_small[i].m10/ContArea_small[i].m00, ContArea_small[i].m01/ContArea_small[i].m00);
cv::Scalar color_small = cv::Scalar(0,255,0);
if(ContArea_small[i].m00 > 2000)
{
drawContours( drawing_small, contours_small, i, color_small, CV_FILLED , 8, hierarchy_small, 1, cv::Point() );
}
}
cv::imwrite("contour.jpg",drawing_small);
cv::dilate(drawing_small, drawing_small,C,cv::Point(-1,-1),1,1,20);
cv::threshold(drawing_small,drawing_small,100,255,cv::THRESH_BINARY_INV);
cv::GaussianBlur(drawing_small,drawing_small,cv::Size(9,9),11);

This probably means that Hough Transform did manage to find any lines on your picture. In this case you should pre-filter your image first. For example, you can try Otsu's thresholding and Gaussian blur. And if I were you than I would first start from trying to pass different parameters to cv::HoughLines (especially threshold -- The minimum number of intersections to “detect” a line)

Make sure you are drawing lines on and outputting the source image instead of some processed one. Can you show us more code about what you did exactly.

Related

Draw vertical HoughLines till certain intersection points

My idea is to draw all vertical lines, which are created by calculating the Canny before, from a intersection point to a diagonal lines to another intersection point (also this point comes from a intersection between a vetical and diagonal line). As a reference here an image, the red vertical (Hough)lines should be drawen:
Until yet I just detect all vertical lines with this implementation:
int main(int argc, char *argv[]) {
std::vector<cv::Point> diagonalLine = DiagonalLines::diagonalLines(src);
Mat wdst, cwdst, contRegion;
vector<Vec4i> vericalLines;
double maxLineGap = 200.0;
double threshold = 100;
std::vector<cv::Vec4i> elemLinesCur;
cv::Scalar mu, sigma;
meanStdDev(src, mu, sigma);
Canny(src, wdst, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0], 3, false);
cvtColor(wdst, cwdst, CV_GRAY2BGR);
HoughLinesP(wdst, vericalLines, 1, CV_PI / 2, threshold, 50, 200);
cv::Vec4i current, previous;
cv::Point pt1, pt2, ppt1, ppt2;
for (size_t i = 1; i < vericalLines.size(); i++) {
current = vericalLines[i];
pt1 = cv::Point(current[0], current[1]);
pt2 = cv::Point(current[2], current[3]);
previous = vericalLines[i - 1];
ppt1 = cv::Point(previous[0], previous[1]);
ppt2 = cv::Point(previous[2], previous[3]);
if (diagonalLine[i - 1].y > pt2.y && diagonalLine[i].y < pt1.y) {
std::cout << "Intersection: " << pt2.x << "\n";
}
double distanceBetweenPointsX = abs(pt1.x - ppt1.x)*sqrt(2);
if (distanceBetweenPointsX >= 12) {
elemLinesCur.push_back(current);
double angle = atan2(ppt2.y - ppt1.y, ppt2.x - ppt1.x) * 180.0 / CV_PI; ///draw only vertical lines (90 degree)
if (angle) {
line(cwdst, pt1, pt2, cv::Scalar(0, 0, 255), 2, CV_AA);
}
//do some stuff
}
...and here a method, which detect only diagonal lines (it looks similiar to the above one):
std::vector<cv::Point> diagonalLines(cv::Mat src) {
std::vector<cv::Point> hitPoint;
Scalar mu, sigma;
meanStdDev(src, mu, sigma);
Canny(src, ddst, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0], 3, false);
cvtColor(ddst, cddst, CV_GRAY2BGR);
HoughLinesP(ddst, vertlines, 1, CV_PI / 180, 100, 50, 10);
cv::Point pt1, pt2;
for (size_t i = 1; i < vertlines.size(); i++) {
cv::Vec4i current = vertlines[i];
pt1 = cv::Point(current[0], current[1]);
pt2 = cv::Point(current[2], current[3]);
double angle = atan2(pt2.y - pt1.y, pt2.x - pt1.x) * 180.0 / CV_PI;
if (angle != -90 && angle != 90) {
//line(cddst, pt1, pt2, Scalar(0, 0, 255), 2, CV_AA);
hitPoint.push_back(pt1);
hitPoint.push_back(pt2);
}
}
return hitPoint;
}
What I know:
I should calculate all those intersection points, yes, I also tried it in if (diagonalLine[i - 1].y > pt2.y && diagonalLine[i].y < pt1.y) but I don't get the further steps. Could some one help me? Thank you in advance!
The OpenCV function line() accepts endpoints as the arguments, so all you need to do is calculate the intersections and use those intersection points for the endpoints of the vertical lines. You can calculate the intersection directly from the endpoints you have as the result of HoughLinesP() using determinants.
In Python, a function to compute the intersection points might look like
def find_intersection(line1, line2):
# extract points
x1, y1 = line1[0]
x2, y2 = line1[1]
x3, y3 = line2[0]
x4, y4 = line2[1]
# compute determinant
Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4)) /
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4)) /
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
return (int(Px), int(Py))
Let's show how you might use this. Suppose your image looked like this:
# draw image and lines
img = np.ones((500, 500, 3)) * 255
diag1 = [(0, 0), (499, 100)]
diag2 = [(0, 499), (499, 399)]
vert1 = [(100, 0), (100, 499)]
vert2 = [(400, 0), (400, 499)]
cv2.line(img, diag1[0], diag1[1], color=[0, 0, 255])
cv2.line(img, diag2[0], diag2[1], color=[0, 0, 255])
cv2.line(img, vert1[0], vert1[1], color=[0, 255, 0])
cv2.line(img, vert2[0], vert2[1], color=[0, 255, 0])
To cut them off at the intersection, simply use the function to find those points and only draw the vertical lines at the intersection points with each diagonal line.
# get intersection points
vert1_intersect = [find_intersection(diag1, vert1), find_intersection(diag2, vert1)]
vert2_intersect = [find_intersection(diag1, vert2), find_intersection(diag2, vert2)]
# draw vertical lines from intersection points
img = np.ones((500, 500, 3)) * 255
diag1 = [(0, 0), (499, 100)]
diag2 = [(0, 499), (499, 399)]
vert1 = [(100, 0), (100, 499)]
vert2 = [(400, 0), (400, 499)]
cv2.line(img, diag1[0], diag1[1], color=[0, 0, 255])
cv2.line(img, diag2[0], diag2[1], color=[0, 0, 255])
cv2.line(img, vert1_intersect[0], vert1_intersect[1], color=[0, 255, 0])
cv2.line(img, vert2_intersect[0], vert2_intersect[1], color=[0, 255, 0])

Removing border lines in opencv and c++

I have many images with and without text similar to the image above. I want to remove the lines at the edges and also remove noise if any present in the image.
These lines are present only at edges as I have cropped these images from a table.
You can try following approach. But i cant guarantee that all lines in your image file can be removed.
First detect all lines present in the image by applying Hough Transform
vector<Vec2f> lines;
HoughLines(img, lines, 1, CV_PI/180, 100, 0, 0 );
Then iterate through each line detected,
Get size of the image
#you may have laoded image to some file
#such as
# Mat img=imread("some_file.jpg");
int rows=img.rows;
int colms=img.cols;
Point pt3;
now you know the size of matrix, next get the centre point of the line, you can do so as below,
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
pt3.x=(pt1.x+pt2.x)/2;
pt3.y=(pt1.y+pt2.y)/2;
***
//decide whether you want to remove the line,i.e change line color to
// white or not
line( img, pt1, pt2, Scalar(255,255,255), 3, CV_AA); // if you want to change
}
***once you have both centre point and size of the image, you can compare the position of the centre point is in left,right,top, bottom. You can do so by comparing with as follows. Don't use (==) allow some difference.
1. (0, cols/2) -- top of the image,
2. (rows/2,0) -- left of the image,
3. (rows, cols/2) -- bottom of the image
4. (rows/2, cols) -- right of the image
(since your image is already blurred, smoothing , erosion and dilation may not do well)
If your images are all the same, then just crop the bottom off using OpenCV...
Alternatively this link demonstrates how to remove black borders from an image.
In order to clean up the text you could try denoising

Cannot detect horizontal lines of an image

I'm writing a mobile app to plot the graphical representation (graphs and charts) of images of statistical data tables. currently i'm writing the table detection module of the project using OpenCV with c++.
I have already applied adaptiveThreshold and Canny to detect the largest Contour and cropped out the table. (https://i.imgur.com/clBS3dr.jpg)
and following is the code i'm using to detect the horizontal and vertical lines: Note: "Crop" is the already cropped table image(Mat)
cvtColor(crop, crop, CV_RGB2GRAY);
adaptiveThreshold(crop, crop, 255, CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY, 31, 15);
Mat dst1, cdst1;
Canny(crop, dst1, 50, 200, 3);
cvtColor(dst1, cdst1, CV_GRAY2BGR);
vector<Vec2f> lines;
// detect lines
HoughLines(dst1, lines, 1, CV_PI/180, 200, 0, 0 );
//HoughLinesP(dst1, lines, 1, CV_PI/180, 150, 0, 0);
// draw lines
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
//if( theta>CV_PI/180*170 || theta<CV_PI/180*10){
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
line( cdst1, pt1, pt2, Scalar(0,0,255), 3, CV_AA);
//}
}
namedWindow("detected lines",WINDOW_NORMAL);
imshow("detected lines", cdst1);
And the result of this code comes out like this : https://i.imgur.com/yDuCqmo.jpg
What am I going wrong to the Horizontal lines only to reach half of the image?
if you are trying to extract each cell in the table you can try contour processing,
Do binary invert threshold in the source.
Find contour, here you should use RETR_EXTERNAL.
Then draw contour with CV_FILLED, here you will get mask for your table. Notice that here you should get only one contour, and assumes there wont be any noise outside the table. Or if you got multiple contour draw largest as mask.
Bitwise xor between threshold and mask
Again Find contour, with RETR_EXTERNAL option. See the drawn contour with CV_FILLED option.
Calculate bounding Rect or Rotated rect for contour for further use.
See bounding rect.
See rotated rect.
I suspect your call to HoughLines is playing a role. If you tweak the threshhold parameter, you can get more appreciable results with increased or decreased lines.

detecting 2 lines opencv

I have an image on which I run a dilation, and works fine, now I want to detect two dick lines on it :
and when run on it the part of code:
cv::Canny(dilationResult,canny,50,200,3);
cv::cvtColor(dilationResult,dilationResult,CV_BGR2GRAY);
cv::HoughLines(canny,lines,30,CV_PI/180,500,0);
cv::cvtColor(mask,mask,CV_GRAY2BGR);
if(lines.size()!=0){
std::cout << " line Size " << lines.size()<< std::endl;
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][2];
cv::Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
angle = atan2f((pt2.y-pt1.y),(pt2.x-pt1.x))*180.0/CV_PI;
std::cout << "angle " << angle<< std::endl;
line( mask, pt1, pt2, cv::Scalar(0,0,255), 3, CV_AA);
}
}
cv::imshow("mask " ,mask);
here's the result:
what I would like to get is something like this :
getting only 2 lines that have the same width, and by the way I don't want to use findcontour function
any idea how can do this !
I don't get it to work with hough transform, but with the probabilistic version cv::HoughLinesP
with lineDetection_Input.jpg being your linked image
int main()
{
cv::Mat color = cv::imread("../lineDetection_Input.jpg");
cv::Mat gray;
cv::cvtColor(color, gray, CV_RGB2GRAY);
std::vector<cv::Vec4i> lines;
cv::HoughLinesP( gray, lines, 1, 2*CV_PI/180, 100, 100, 50 );
for( size_t i = 0; i < lines.size(); i++ )
{
cv::line( color, cv::Point(lines[i][0], lines[i][1]),
cv::Point(lines[i][2], lines[i][3]), cv::Scalar(0,0,255), 1);
}
cv::imwrite("lineDetection_Output.jpg", color);
cv::namedWindow("output"); cv::imshow("output", color); cv::waitKey(-1);
return 0;
}
lineDetection_Output.jpg:
for rotated image:
and for some different intersection angle:
there you can see some lines detected with a slightly false angle that start in the top-right and end near the intersection (close behind), but these might be easily filtered by length or something.

logic behind the code

this is from opencv hough lines, can any one explain me, after changing it tio cartesian
WHY THEY ADDED a+1000, -b*1000
#include <cv.h>
#include <highgui.h>
#include <math.h>
int main(int argc, char** argv)
{
IplImage* src;
if( argc == 2 && (src=cvLoadImage(argv[1], 0))!= 0)
{
IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
int i;
cvCanny( src, dst, 50, 200, 3 );
cvCvtColor( dst, color_dst, CV_GRAY2BGR );
#if 1
lines = cvHoughLines2( dst,
storage,
CV_HOUGH_STANDARD,
1,
CV_PI/180,
100,
0,
0 );
for( i = 0; i < MIN(lines->total,100); i++ )
{
float* line = (float*)cvGetSeqElem(lines,i);
float rho = line[0];
float theta = line[1];
CvPoint pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
}
Cos and Sin go from -1 to +1, so the origin of the Hough accumalator space is at 0,0.
Assuming your display has positive size it's convenient to have the centre of the plot in the middle of the screen.
Perhaps they wanted to get corners of the bounding rectangle around a given center?
It is a hack.
Try this. Run the example as is. Remove the 4 instances of 1000. You will get points instead of lines.
Put in 750 instead of 1000. You get the same result as if you had put in 1000.
The 1000 is to make sure the lines get drawn across the image. You could also do the following, which is
a little better:
Right after HoughLines(...) is called, add the following:
int h = src.rows;
int w = src.cols;
int factor = (int) (sqrt(h * h + w * w)); // diagonal length of the image, maximum line length
Then instead of 1000, multiply by factor. If your image is greater than 1000x1000, the original
code won't work.
Roy