Why cannot draw lines after Canny Edge Detection? - opencv

Actually, I am noob for working with Computer Vision. Sorry in advance.
I want to detect edges of tram lane. Mostly, the code works well but sometimes It cannot even draw a line. I don't know why.
cropped_Image function is just cropping the polygonal area of the current frame.
display_lines function draw lines whose absolute value of angle is between 30 and 90. It uses cv2.line to draw lines.
Here is the code:
_,frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) # convert image to gray to be one layer
blur = cv2.GaussianBlur(gray, (1, 1), 0) # to reduce noise in gray scale image
canny = cv2.Canny(blur, 150, 200, apertureSize=3)
cropped_image = region_of_interest(canny) # simply, it crops bottom of image
lines = cv2.HoughLinesP(cropped_image, 1, np.pi / 180, 100, np.array([]),
minLineLength=5, maxLineGap=5)
hough_bundler = HoughBundler()
lines_merged = hough_bundler.process_lines(lines, cropped_image)
line_image = display_lines(frame, lines_merged)
combo_image = cv2.addWeighted(frame, 0.8, line_image, 1, 1)
cv2.imshow(‘test’, combo_image)
To see it: HoughBundler
Expected: expected img
Canny: canny img of wrong result
Result: wrong result

First of all I'd start by fixing the cv2.GuassianBlur() line. You've used a 1x1 kernel which doesn't do anything, you need to use at least a 3x3 kernel. Look into how convolutions are applied if you want to know why a 1x1 filter doesn't work.
Secondly, I would play with the Canny aperture size to suit my needs. Also after edge detection you can use cv2.erode() with a 3x3 or 5x5 kernel so that you don't get a broken line in the image.

Related

How to obtain long edge between black and white thresholds?

Using OpenCv I was converted green areas to white(255) and black(0): see CURRENT OUTPUT.
Using Canny, Laplacian and Sobel edge detection yielded many little edges, instead of a long consistent edge see CANNY EDGE Example.
How could I achieve one long edge as seen in the desired output?
Relevant Code:
image = cv.imread('grass pic.jpg')
lane_image = np.copy(image)
pic = cv.cvtColor(lane_image ,cv.COLOR_BGR2RGB)
lower = np.array([24,0,0])
upper = np.array([177, 194, 20])
green_selection = cv.inRange(pic, lower, upper)
canny= cv.Canny(green_selection,50,150)
plt.imshow(canny, cmap='gray')
CURRENT OUTPUT:
CANNY EDGE Example
DESIRED OUTPUT:
Converting the image to HSV colour space might help you to detect your desired line.
import cv2
import numpy as np
image = cv2.imread("image.png")
HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low = np.array([23, 98, 0])
high = np.array([253, 255, 255])
mask = cv2.inRange(HSV, low, high)
cv2.imshow("mask", mask)
cv2.imwrite("mask.png", mask)
result = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("result", result)
cv2.imwrite("result.png", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask Image
Result Image

houghlinesp and thresholding

I am using opencv Houghlinesp to detect lines in a parking lot. Here is the source image
When I did a hough transform-p to detect the lines, I got final image like this.
It did detect empty spaces. Any ideas how these noisy lines on top of the cars can be removed? Or any direction on alternative algorithms or approaches highly appreciated.
img = cv.imread('Parking-Lot.jpg')
threshold=100
minLineLength = 60
rho=2
maxLineGap=20
theta = np.pi/180
edges = cv.Canny(img, 100, 200)
lines = cv.HoughLinesP(edges, rho, theta, threshold, np.array([]), minLineLength =minLineLength , maxLineGap=maxLineGap)
for i in range(len(lines)):
for line in lines[i]:
cv.line(img, (line[0],line[1]), (line[2],line[3]), (0,255,0), 2)
cv2.imwrite("lines.jpg", img)
You can remove most of the noise by thresholding your image before you apply the edge detection. That way you will remove (most of) the cars and keep your white space lines you are interested in:
import cv2
import numpy as np
img = cv2.imread('Parking-Lot.jpg')
threshold=100
minLineLength = 60
rho=2
maxLineGap=20
theta = np.pi/180
# here you convert the image to grayscale and then threshhold to binary
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,180,255,cv2.THRESH_BINARY)
# continue with the threshholded image instead
edges = cv2.Canny(thresh, 100, 200)
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]), minLineLength =minLineLength , maxLineGap=maxLineGap)
for i in range(len(lines)):
for line in lines[i]:
cv2.line(img, (line[0],line[1]), (line[2],line[3]), (0,255,0), 2)
cv2.imwrite("lines.jpg", img)
This will yield you a much cleaner result:
Feel free to experiment with the threshold parameters; you will need to find a threshold that excludes most of the cars while keeping all the lines that you want to detect.

How to get the area of the contours?

I have a picture like this:
And then I transform it into binary image and use canny to detect edge of the picture:
gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)
edge = Image.fromarray(edges)
And then I get the result as:
I want to get the area of 2 like this:
My solution is to use HoughLines to find lines in the picture and calculate the area of triangle formed by lines. However, this way is not precise because the closed area is not a standard triangle. How to get the area of region 2?
A simple approach using floodFill and countNonZero could be the following code snippet. My standard quote on contourArea from the help:
The function computes a contour area. Similarly to moments, the area is computed using the Green formula. Thus, the returned area and the number of non-zero pixels, if you draw the contour using drawContours or fillPoly, can be different. Also, the function will most certainly give a wrong results for contours with self-intersections.
Code:
import cv2
import numpy as np
# Input image
img = cv2.imread('images/YMMEE.jpg', cv2.IMREAD_GRAYSCALE)
# Needed due to JPG artifacts
_, temp = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
# Dilate to better detect contours
temp = cv2.dilate(temp, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
# Find largest contour
cnts, _ = cv2.findContours(temp, cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_NONE)
largestCnt = []
for cnt in cnts:
if (len(cnt) > len(largestCnt)):
largestCnt = cnt
# Determine center of area of largest contour
M = cv2.moments(largestCnt)
x = int(M["m10"] / M["m00"])
y = int(M["m01"] / M["m00"])
# Initiale mask for flood filling
width, height = temp.shape
mask = img2 = np.ones((width + 2, height + 2), np.uint8) * 255
mask[1:width, 1:height] = 0
# Generate intermediate image, draw largest contour, flood filled
temp = np.zeros(temp.shape, np.uint8)
temp = cv2.drawContours(temp, largestCnt, -1, 255, cv2.FILLED)
_, temp, mask, _ = cv2.floodFill(temp, mask, (x, y), 255)
temp = cv2.morphologyEx(temp, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
# Count pixels in desired region
area = cv2.countNonZero(temp)
# Put result on original image
img = cv2.putText(img, str(area), (x, y), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, 255)
cv2.imshow('Input', img)
cv2.imshow('Temp image', temp)
cv2.waitKey(0)
Temporary image:
Result image:
Caveat: findContours has some problems one the right side, where the line is very close to the bottom image border, resulting in possibly omitting some pixels.
Disclaimer: I'm new to Python in general, and specially to the Python API of OpenCV (C++ for the win). Comments, improvements, highlighting Python no-gos are highly welcome!
There is a very simple way to find this area, if you take some assumptions that are met in the example image:
The area to be found is bounded on top by a line
Any additional lines in the image are above the line of interest
There are no discontinuities in the line
In this case, the area of the region of interest is given by the sum of the lengths from the bottom of the image to the first set pixel. We can compute this with:
import numpy as np
import matplotlib.pyplot as pp
img = pp.imread('/home/cris/tmp/YMMEE.jpg')
img = np.flip(img, axis=0)
pos = np.argmax(img, axis=0)
area = np.sum(pos)
print('Area = %d\n'%area)
This prints Area = 22040.
np.argmax finds the first set pixel on each column of the image, returning the index. By first using np.flip, we flip this axis so that the first pixel is actually the one on the bottom. The index corresponds to the number of pixels between the bottom of the image and the line (not including the set pixel).
Thus, we're computing the area under the line. If you need to include the line itself in the area, add pos.shape[0] to the area (i.e. the number of columns).

Reduce the image to the text contents using scikit image

Here is the image from which I want to take the text out.
How to remove the black border and reduce the image to only 50?
Approach I took:
I tried to use corner detectors (corner peak and corner harris) and pick the first 2 coordinates from the left and last 2 coordinates from the right.
With those 4 coordinates I cropped the image and I further reduced by 5 on all sides.
Certainly not efficient way of doing it. I also looked at few segmentation also. Not able to get it right. I am using scikit image for solving this.
Using corners might not work since corner points can also be present in characters.
Here is what i tried with hough lines as described below:
1) First erode the image to minimize the gap between lines and characters
2) Use Hough line detection algorithm to detect and delete the lines
3) Dilate the image to get clear characters
4) Now we have characters and lines separated, so we can delete the lines by finding the connected components.
Here is the code implementation of the same in Python:
img = cv2.imread('D:\Image\st1.png',0)
ret, thresh = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY_INV)
#dilate the image to reduce gap between characters and lines and get hough lines correctly
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(thresh,kernel,iterations = 1)
#find canny edge image
canny = cv2.Canny(erosion,100,200)
minLineLength=img.shape[1]/4
lines = cv2.HoughLinesP(image=canny,rho=0.02,theta=np.pi/500, threshold=10,lines=np.array([]), minLineLength=minLineLength,maxLineGap=10)
a,b,c = lines.shape
# delete the lines
for i in range(a):
cv2.line(erosion, (lines[i][0][0], lines[i][0][1]), (lines[i][0][2], lines[i][0][3]), 0, 3, cv2.LINE_AA)
#erode the image
kernel = np.ones((3,3),np.uint8)
erosion = cv2.dilate(erosion, kernel, iterations=1)
# find connected components
connectivity = 4
nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(erosion, connectivity, cv2.CV_32S)
sizes = stats[1:, -1]; nb_components = nb_components - 1
min_size = 250 #threshhold value for lines length
img2 = np.zeros((output.shape), np.uint8)
for i in range(0, nb_components):
if sizes[i] >= min_size:
img2[output == i + 1] = 255 #delete the line components
img = cv2.bitwise_not(img2)
Output image:

HoughCircles Parameters to recognise balls

After processing an image by converting it to grey scale and then blurring it, I'm trying to apply a Hough Circle Transformation with these parameters:
CV_HOUGH_GRADIENT
dp = 1
min_dist = 1
param_1 = 70
param_2 = 100
min_radius = 0
max_radius = 0
Here is one of the many images I've tried:
http://i.stack.imgur.com/JGRiM.jpg
But the algorithm fails to recognise the ball even with relaxed parameters.
(When I try it with an image of a circle created in GIMP it works fine)
I agree with krzych.
I had it working effortlessly with :
cv::Mat img,img2;
std::vector<cv::Vec3f> circles;
img = cv::imread("JGRiM.jpg",1);
cv::bilateralFilter(img, img2, 15, 1000, 1000);
cv::cvtColor(img2, img2,CV_BGR2GRAY);
cv::HoughCircles(img2, circles, CV_HOUGH_GRADIENT, 1,300,50, 10);
cv::circle(img2,cv::Point(circles[0][0],circles[0][1]),circles[0][2],cv::Scalar(126),2);
cv::imshow("test",img2);
cv::waitKey(0);
cv::imwrite("test.jpg",img2);
return 0;
Good luck :)
Check Canny output of your images first. From this Canny output it is possible to detect ball with very small param_2 as well as many false circles on image. (I've used for example param_2 = 10, and with specified ball center to eliminate false circles it works)
Try to help Hough Circle Transform. The task is to segment ball from other elements. In your image problem is line, you can try to segment ball using colours for example.

Resources