Finding the width of Cars using OpenCV - python

I want to detect the width (in pixels) of any car in front of me. So, I tried to extract the horizontal lines which is supposed to correspond to its width. Below is my approach:
input image :
import cv2
from google.colab.patches import cv2_imshow
import numpy as np
from google.colab.patches import cv2_imshow
image = cv2.imread('croopped.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 250, apertureSize=3)
cv2_imshow(edges)
kernel = np.ones((1,5))
linh = cv2.erode(edges,kernel)
cv2_imshow(linh)
lines = cv2.HoughLinesP(linh, 1, np.pi/180, 10, maxLineGap= 50, minLineLength = 50)
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 3)
cv2_imshow(image)
As you can see the lines are not as expected (to get a good horizontal line corresponds to its width)
expected results:
How can I fine tune/use different approach to get better horizontal lines?

You can try getting the vertical lines instead, and then measure their distance as shown in the code:
kernel = np.ones((1,5))
linh = cv2.erode(edges,kernel)
cv2_imshow(linh)
Not sure how you would modify the code for that case though.

Related

Error, while opening Numpy array in OpenCV

I am currently stuck on detecting the hough lines in a precalculated NumPy array.
However, it seems like I cannot figure out, why OpenCV doesn't accept the NumPy array to work with and detect the lines.
The error message refers to the input channels, even so, I thought this should be detected automatically since OpenCV uses NumPy arrays natively.
I provided a minimum working example below.
However, the line detection might fail because of the random values.
import numpy as np
import cv2
from matplotlib import pyplot as plt
format = 50
img = np.random.choice([0, 1], size=(format,format), p=[1/30, 29/30])
plt.imshow(img, interpolation='nearest')
plt.show()
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,150,apertureSize = 3)
minLineLength = 100
maxLineGap = 10
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for x1,y1,x2,y2 in lines[0]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv2.imshow('image',img)
cv2.waitKey(0)
Error Message:
> cv2.error: OpenCV(4.2.0)
> ../modules/imgproc/src/color.simd_helpers.hpp:92: error:
> (-2:Unspecified error) in function
> 'cv::impl::{anonymous}::CvtHelper<VScn, VDcn, VDepth,
> sizePolicy>::CvtHelper(cv::InputArray, cv::OutputArray, int) [with
> VScn = cv::impl::{anonymous}::Set<3, 4>; VDcn =
> cv::impl::{anonymous}::Set<1>; VDepth = cv::impl::{anonymous}::Set<0,
> 2, 5>; cv::impl::{anonymous}::SizePolicy sizePolicy =
> cv::impl::<unnamed>::NONE; cv::InputArray = const cv::_InputArray&;
> cv::OutputArray = const cv::_OutputArray&]'
> Invalid number of channels in input image:
> 'VScn::contains(scn)'
> where
> 'scn' is 1
Edit:
I know the problem is with the numpy array being of binary data (0 or 1), but I don't know how to read it in to be handled properly.
As written in the comments there are several flaws in your code.
Use an image size where Houghline has a chance to find lines.
For an image the np.random.choise array has to be converted to unit8.
The result can be used as grayscale image.
The colour "white" in grayscale image has value 255.
If you want to draw colours, you have to convert your image to BGR.
White noise will hardly lead to detected lines. I have intentionally drawn a line through the noise to demonstrate, that houghlines can distinguish this line from the noise.
You can use this working example to start from and edit it step by step to what you actually want to do.
import numpy as np
import cv2
f = 500
img = np.random.choice([255, 0], size=(f, f), p=[1/30, 29/30]).astype("uint8")
cv2.line(img, (50, 50), (100, 300), 255, 2)
cv2.imshow('image', img)
cv2.waitKey(0)
minLineLength = 100
maxLineGap = 40
lines = cv2.HoughLinesP(img, 1, np.pi/180, 100, minLineLength, maxLineGap)
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
if lines is not None:
for x1, y1, x2, y2 in lines[0]:
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow('image', img)
cv2.waitKey(0)
Output:

How to fit a jagged binary image with a straight line?

I have a binary image like the following:
I want to find a straight line to fit the jagged edge, like this:
What will be a good way to do it? The fitting does not need to be precise, and speed is more critical in my case.
In order to find corners in your image you can use the goodFeaturesToTrack function in CV2.
When you find the corners, you should consider the corners close to the jagged edge.
Then, you can draw a straight line between:
If the jagged edge is horizontal - the left most corner to the right.
If the edge is vertical - from the highest to lowest corner.
# Required libraries
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# First, read the image
img = cv2.imread("kitT9.png", cv2.COLOR_BGR2GRAY)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Order the parameters for the `cv.goodFeaturesToTrack` function
input_img = gray
maxCorners = 20
qualityLevel = 0.01
minDistance = 1
corners = cv.goodFeaturesToTrack(input_img, maxCorners, qualityLevel, minDistance)
# This is useful for debugging: draws the corners on the image.
corners = np.int0(corners)
for i in corners:
x, y = i.ravel()
cv.circle(img, (x, y), 3, 255, -1)
# This part sorts the corners from left to right.
def sorter(x):
return x[0][0]
corners = sorted(corners, key=sorter)
# Extract the coordinates of the leftmost and rightmost corners
left_corner = corners[0]
right_corner = corners[-1]
x1 = left_corner[0][0]
y1 = left_corner[0][1]
x2 = right_corner[0][0]
y2 = right_corner[0][1]
# Draw the required line!
line_thickness = 2
img = cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), thickness=line_thickness)
# In a notebook, show the final result
plt.imshow(img), plt.show()
The output result

Probabilistic Hough lines returning only angled lines

I'm attempting to parse floorplans to turn them into an array of line coordinates using HoughLinesP in opencv-python and the function is only returning lines that are angled and have no relation to the actual lines in my image.
Here is my code:
import cv2
# Read image and convert to grayscale
img = cv2.imread('C:/Data/images/floorplan/extremely basic.png', -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Get lines with probabilistic hough lines
found_lines = cv2.HoughLinesP(gray, 1, 3.14 / 160, 100,
minLineLength=1, maxLineGap=10)
# Loop through found lines and draw each line on original image
for line in found_lines:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1)
# Show image, wait until keypress, close windows.
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
And here is what's going to be returned with threshold 150 and 100 respectively:
I've tried tinkering with all options and attempted non-probabilistic Hough lines to no avail.
The problem was with image inversion and parameters. You have to do further adjustments as this does not give all lines.
The code is test on google colab. Remove from google.colab.patches import cv2_imshow and replace cv_imshow with cv2.imshow for local usage.
Partial Image Ouput
Code
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
# Read image and convert to grayscale
img = cv2.imread('1.jpg', 0)
#gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
s1, s2 = img.shape
new_img = np.zeros([s1,s2],dtype=np.uint8)
new_img.fill(255)
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
cv2_imshow(thresh1)
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(thresh1,kernel,iterations = 1)
cv2_imshow(erosion)
# Get lines with probabilistic hough lines
found_lines = cv2.HoughLinesP(erosion, np.pi/180, np.pi/180, 10, minLineLength=4, maxLineGap=4)
# Loop through found lines and draw each line on original image
for line in found_lines:
x1, y1, x2, y2 = line[0]
cv2.line(new_img, (x1, y1), (x2, y2), (0, 0, 255), 1)
cv2_imshow(new_img)
#cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1)
# Show image, wait until keypress, close windows.
print("ORIGINAL IMAGE:")
cv2_imshow(img)
#cv2.imshow('image', img)
#cv2.waitKey(0)
#cv2.destroyAllWindows()

Detecting the biggest rectangle with thicker lines on grid paper with OpenCV

We are making a game where you can use grid paper to make your own level.
The first step was detecting the boundaries of the level, so I made a thicker black border around the level for the edges.
This is the image: https://imgur.com/a/Wx3p7Ci
I first edited the image to gray, applied GaussianBlur and adaptiveThreshold.
This is the result: https://imgur.com/a/dp0G4my
Normal Houghlines isn't really an option I think as it detects so many lines and loses the thicker lines.
findContour I haven't got a usable outcomes as there are so many shapes in a gridpaper.
So I ended up with HoughLinesP which resulted in this:https://imgur.com/a/yvIpYNy
So it detects one part of the rectangle correctly and that is it. I would have another idea which would rely on colors which isn't ideal (people would need a specific color) but I'm at a loss on what a good approach for this would be.
Here is my code I currently have for HoughLinesP
import cv2 as cv
import numpy as np
# from openCV.main import contours
image = cv.imread("grey-grid-blackborder.jpg")
cv.imshow("Image", image)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
cv.imshow("Gray", gray)
blur = cv.GaussianBlur(gray, (5, 5), 0)
cv.imshow("blur", blur)
thresh = cv.adaptiveThreshold(blur, 255, 1, 1, 11, 2)
cv.imshow("thresh", thresh)
grid = image
minLineLength = 1
maxLineGap = 0
# lines = cv.HoughLinesP(thresh, 1, np.pi / 180, 100, minLineLength, maxLineGap)
lines = cv.HoughLinesP(thresh, 1, np.pi / 180, 100, minLineLength=minLineLength, maxLineGap=maxLineGap)
for x1, y1, x2, y2 in lines[0]:
cv.line(grid, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv.imshow("grid", grid)
cv.waitKey(0)

Opencv trace lines from dotted image

I have following image: named 'Normalised.png'. I am trying to draw solid lines from dotted lines.
I have tried approaches like hough line transform:
import cv2
import numpy as np
img = cv2.imread('Normalised.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,150,apertureSize = 3)
minLineLength = 100
maxLineGap = 10
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for x1,y1,x2,y2 in lines[0]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv2.imwrite('houghlines5.jpg',img)
But it appears that the code fails on 'edges' as no 'edges' are detected.
Input image
Expected Output Image
How do I achieve this output?
By default, HoughLinesP works for straight lines. However, you can detect curves by using cv2.HOUGH_PROBABILISTIC as follows:
img = cv.imread("Dilate.png")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 150, 200, apertureSize=3)
cv.imwrite("Canny.png", edges)
element = cv.getStructuringElement(cv.MORPH_RECT, (5, 3), (-1, -1))
dilated = cv.dilate(edges, element)
cv.imwrite("Eroded.png", dilated)
minLineLength = 200
maxLineGap = 5
lines = cv.HoughLinesP(dilated, cv.HOUGH_PROBABILISTIC, np.pi/180, 150, minLineLength,
maxLineGap)
for x in range(0, len(lines)):
for x1, y1, x2, y2 in lines[x]:
pts = np.array([[x1, y1], [x2, y2]], np.int32)
cv.polylines(img, [pts], True, (0, 255, 0))
cv.imwrite('dilate_final.png', img)
Note how the lines are being drawn.
Result is not exactly what you want but close and requires you to tune the parameters which I will leave for you. Hope it helps!
A possible scheme (though the whole task seems desperate):
choose a small number of directions (say 5) uniformly spread;
for every direction,
smooth in that direction (f.i. with a very elongated Gaussian) or
erode in that direction (with a linear structuring element), or both, to better connect the dots,
binarize with threshold such that the dots come in contact,
apply morphological thickening to get thin black lines.
combine all maps so obtained (max operation),
cleanup.

Categories

Resources