Convert an opencv script - python

I found this code from this website (https://www.hackster.io/tinkernut/raspberry-pi-smart-car-8641ca) and try to rewrite it so that it works with a USB webcam and not with the PI camera.
I wanted to add the recorded material to a Numpy array, but I can't get any further. After a lot of fiddling around and searching the internet I didn't find anything to solve the problem.
I don't have much experience in Python or opencv programming so I hope someone can help me because I am overwhelmed and my brain just feels like a crushed potato.
Generally to my project, I want to build an infotainment system with a Raspberry Pi and everything works except the rear view camera. So navigation, music and video playback and so on.
As main program to use all other programs I use Kodi with the CarPc-Carbonskin.
Translated with www.DeepL.com/Translator
import time
import cv2
import numpy as np
from picamera.array import PiRGBArray
from picamera import PiCamera
import RPi.GPIO as GPIO
buzzer = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(buzzer, GPIO.OUT)
camera = PiCamera()
camera.resolution = (320, 240) #a smaller resolution means faster processing
camera.framerate = 24
rawCapture = PiRGBArray(camera, size=(320, 240))
kernel = np.ones((2,2),np.uint8)
time.sleep(0.1)
for still in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
GPIO.output(buzzer, False)
image = still.array
#create a detection area
widthAlert = np.size(image, 1) #get width of image
heightAlert = np.size(image, 0) #get height of image
yAlert = (heightAlert/2) + 100 #determine y coordinates for area
cv2.line(image, (0,yAlert), (widthAlert,yAlert),(0,0,255),2) #draw a line to show area
lower = [1, 0, 20]
upper = [60, 40, 200]
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
#use the color range to create a mask for the image and apply it to the image
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask=mask)
dilation = cv2.dilate(mask, kernel, iterations = 3)
closing = cv2.morphologyEx(dilation, cv2.MORPH_GRADIENT, kernel)
closing = cv2.morphologyEx(dilation, cv2.MORPH_CLOSE, kernel)
edge = cv2.Canny(closing, 175, 175)
contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
threshold_area = 400
centres = []
if len(contours) !=0:
for x in contours:
#find the area of each contour
area = cv2.contourArea(x)
#find the center of each contour
moments = cv2.moments(x)
#weed out the contours that are less than our threshold
if area > threshold_area:
(x,y,w,h) = cv2.boundingRect(x)
centerX = (x+x+w)/2
centerY = (y+y+h)/2
cv2.circle(image,(centerX, centerY), 7, (255, 255, 255), -1)
if ((y+h) > yAlert):
cv2.putText(image, "ALERT!", (centerX -20, centerY -20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255),2)
GPIO.output(buzzer, True)
cv2.imshow("Display", image)
rawCapture.truncate(0)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
GPIO.output(buzzer, False)
break

Related

OpenCV - Draw contours on fingers using convex-hulls & adaptive thresholding

I am pretty new to OpenCV and am trying to achieve drawing simple contours along the outline of my hand using a webcam. I decided on using cv2.adaptiveThreshold() to deal with the different light intensities when the camera adjusts to the hand moving. Everything seems to work fine except that it is struggling with finding the fingers and then also drawing closed contours.
See here:
I thought about trying to detect a convex hull and detect anything deviating from it somehow.
How do I go about this best? Firstly I need to manage to maybe not find weird closed contours and then go from there?
Here's the code, I fixed the trackbar values for you :)
import cv2
import numpy as np
#####################################
winWidth = 640
winHeight = 840
brightness = 100
cap = cv2.VideoCapture(0)
cap.set(3, winWidth)
cap.set(4, winHeight)
cap.set(10, brightness)
kernel = (7, 7)
#######################################################################
def empty(a):
pass
cv2.namedWindow("TrackBars")
cv2.resizeWindow("TrackBars", 640, 240)
cv2.createTrackbar("cVal", "TrackBars", 10, 40, empty)
cv2.createTrackbar("bSize", "TrackBars", 77, 154, empty)
def preprocessing(frame, value_BSize, cVal):
imgGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# mask = cv2.inRange(imgHsv, lower, upper)
imgBlurred = cv2.GaussianBlur(imgGray, kernel, 4)
gaussC = cv2.adaptiveThreshold(imgBlurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, value_BSize,
cVal)
imgDial = cv2.dilate(gaussC, kernel, iterations=3)
imgErode = cv2.erode(imgDial, kernel, iterations=1)
return imgDial
def getContours(imPrePro):
contours, hierarchy = cv2.findContours(imPrePro, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 60:
cv2.drawContours(imgCon, cnt, -1, (0, 255, 0), 2, cv2.FONT_HERSHEY_SIMPLEX)
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
#######################################################################################################
while cap.isOpened():
success, frame = cap.read()
cVal = cv2.getTrackbarPos("cVal", "TrackBars")
bVal = cv2.getTrackbarPos("bVal", "TrackBars")
value_BSize = cv2.getTrackbarPos("bSize", "TrackBars")
value_BSize = max(3, value_BSize)
if (value_BSize % 2 == 0):
value_BSize += 1
if success == True:
frame = cv2.flip(frame, 1)
imgCon = frame.copy()
imPrePro = preprocessing(frame, value_BSize, cVal)
getContours(imPrePro)
cv2.imshow("Preprocessed", imPrePro)
cv2.imshow("Original", imgCon)
if cv2.waitKey(1) & 0xFF == ord("q"):
cv2.destroyAllWindows()
break
L*a*b color space can help find objects brighter than the background. One advantage is that color space is hardware independent, so it should yield relatively similar results from any camera. Using the OTSU option to threshold the image can help it work in different lightning conditions, as it calculates the optimal threshold intensity to separate bright and dark areas in the image. Obviously it is not a silver bullet and will NOT work perfectly in every situation, especially in extreme cases, but as long your hand's brightness is relatively different from the background, it should work.
lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
tv, thresh = cv2.threshold(lab[:,:,0], 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
plt.imshow(thresh)
Once the hand is properly thresholded, you can proceed to find the contours and do your analysis as needed.
Note: the artifacts in the threholded image are caused by removing the green contour lines from the original posted image.
I'm using a light threshold so this might work differently depending on the image, but here's what works for this one.
import cv2
import numpy as np
# load image
img = cv2.imread("hand.jpg");
# lab
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB);
l,a,b = cv2.split(lab);
# threshold
thresh = cv2.inRange(l, 90, 255);
# contour
_, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
# filter contours by size
marked = img.copy();
cv2.drawContours(marked, contours, -1, (0, 255, 0), 3);
# show
cv2.imshow("marked", marked);
cv2.imshow("Thresh", thresh);
cv2.waitKey(0);
# save
cv2.imwrite("marked_hand.png", marked);
Have you looked into google's mediapipe, as an alternate to opencv?
Also, I wonder if adding a thin bottom black border to the frame would help the contour to know to wrap around the wrist.

OpenCV - Adaptive thresholding / Trackbar manipulation

I am still new to OpenCV(Python) and am trying out cv2.adaptiveThreshold() to draw proper contours with a webcam running when lighting is changing. The main problem is the insane amount of noise I am getting when drawing contours so I tried to set a cv2.countourArea() threshold but this seems not like the best solution.
Later on I deciced to try and manipulate the values of cv2.adaptiveThreshold with a simple trackbar.
Specifically the blockSize and CValue. Everything works fine on the CValue but I do really struggle on the blockSize since it needs to be an odd number. I tried something along the line of checking if the value of the empty callback function is even and adding +1. But this does not seem to work properly. Later on I will most likely use machine-learning to change these values but for now I'd like the trackbars to work for debugging purposes.
What is the best solution here to manipulate the blockSize with a trackbar?
Thank you in advance! :)
import cv2
import numpy as np
#####################################
winWidth = 640
winHeight = 840
brightness = 100
cap = cv2.VideoCapture(0)
cap.set(3, winWidth)
cap.set(4, winHeight)
cap.set(10, brightness)
kernel = (5, 5)
bSize_default = 1
#######################################################################
def empty(a):
pass
cv2.namedWindow("TrackBars")
cv2.resizeWindow("TrackBars", 640, 240)
cv2.createTrackbar("cVal", "TrackBars", 2, 20, empty)
def preprocessing(frame, cVal):
imgGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# mask = cv2.inRange(imgHsv, lower, upper)
imgBlurred = cv2.GaussianBlur(imgGray, kernel, 3)
gaussC = cv2.adaptiveThreshold(imgBlurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, cVal)
imgDial = cv2.dilate(gaussC, kernel, iterations=3)
imgErode = cv2.erode(imgDial, kernel, iterations=1)
return imgDial
def getContours(imPrePro):
contours, hierarchy = cv2.findContours(imPrePro, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 60:
cv2.drawContours(imgCon, cnt, -1, (255, 0, 0), 3)
#######################################################################################################
while (cap.isOpened()):
success, frame = cap.read()
cVal = cv2.getTrackbarPos("cVal", "TrackBars")
if success == True:
frame = cv2.flip(frame, 1)
imgCon = frame.copy()
imPrePro = preprocessing(frame, cVal)
getContours(imPrePro)
cv2.imshow("Preprocessed", imPrePro)
cv2.imshow("Original", imgCon)
if cv2.waitKey(1) & 0xFF == ord("q"):
cv2.destroyAllWindows()
break
The minimum value of blocksize must be 3 and also blocksize must be odd therefore:
value_BSize= cv2.getTrackbarPos("bSize", "TrackBars")
value_BSize = max(3,value_BSize)
if (value_BSize % 2 == 0):
value_BSize += 1
tbar = (cv2.getTrackbarPos('trackbar', 'window')&~1)+3 # for smooth operation
tbar = cv2.getTrackbarPos('trackbar', 'window')*2+3 # for fast operation

ball detection in python runs very slow, any tips on impoving it?

I am working on a sensor for PID controler, so basically I have a camera recording a scene. In order for control to work I need to extract tennis ball's position on a scene in each frame. Scene is set with white background and orange tennis ball as you can see on image:
And when I run it on my lenovo ideapad 100 (which is quite slow pc) I get ball's position each let's say 1.2 secs.
I think this could be done way faster but I don't know what to do.
Any tips and suggestions are welcome.
Im expecting a system that can generate at least 2 position each second
Here is my code:
import numpy as np
import cv2
import time
cap = cv2.VideoCapture(0)
lower_red = np.array([0,100,100])
upper_red = np.array([20,255,255])
while(1):
start = time.time()
# Load an color image in grayscale
_,img = cap.read()
print("read")
#get the image's width and height
blurred = cv2.GaussianBlur(img, (11, 11), 0)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hsv2 = cv2.cvtColor(blurred,cv2.COLOR_BGR2HSV)
mask2 = cv2.inRange(hsv2, lower_red, upper_red)
mask2 = cv2.erode(mask2, None, iterations=2)
mask2 = cv2.dilate(mask2, None, iterations=2)
imageWidth = mask2.shape[1]
imageHeight = mask2.shape[0]
sum_i_white = 1
num_white = 1
sum_j_white = 1
for i in range(0,imageHeight):
for j in range(0,imageWidth):
if mask2[i][j] == 255:
sum_i_white += i
sum_j_white += j
num_white += 1
cv2.circle(img,(int(sum_j_white/num_white),int(sum_i_white/num_white)),3,(0,0,255), -1)
cv2.putText(img,'here',(int(sum_j_white/num_white),int(sum_i_white/num_white)), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 1, cv2.LINE_AA)
cv2.imshow("final",img)
print(time.time()-start)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
Do you know what is slow?
I know you can run C program with Python.
So, if your double for is very slow, you can make a C program running this.
If you have juste array and int as input, and int as output, I think it shouldn't be hard.
But I don't know if this would be useful.

OpenCV: Track co-ordinates of expanding rectangle

I have a calibration process in which I want to get the maximum height and width of the screen. So I made a growing rectangle of which I want co-ordinates by image processing. This rectangle will always be viewed at an angle.
I have used a coloured rectangle to detect it in HSV range but it doesn't seems to be working. First I detect the green coloured rectangle then threshold it then detect canny edges then find contours and filter the largest contour. Final contour with approximation is shown in 'img' tab in screenshot.
The problem here is the further edge of the green rectangle appears black and doesn't get detected in HSV range. So the maximum width and height is not obtained. Even if I have the top-left and bottom-right corner of the rectangle in edge detection.
Is there a way other to track scaling rectangle other than detecting HSV range of coloured rectangle. Since the rectangle is moving there should be.
CODE:
video = cv2.VideoCapture('rectCalibration2.mp4')
img = np.zeros((360,640,3))
prevImg = np.zeros((360,640,3))
finalContour = []
end_time = time.time() + 15
largestArea = 5000
while(video.isOpened()):
_, frame = video.read()
if frame is None:
print('ended')
break
frame = cv2.resize(frame, (640, 360))
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
greenLower = (55, 100, 6)
greenUpper = (70, 255, 255)
mask = cv2.inRange(hsv, greenLower, greenUpper)
th = cv2.adaptiveThreshold(mask,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)
th = cv2.erode(th, None, iterations=3)
th = cv2.dilate(th, None, iterations=3)
edges = cv2.Canny(th,200,400)
m, contours, hierarchy =
cv2.findContours(edges,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
largeContours = []
for contour in contours:
area = cv2.contourArea(contour)
if area > largestArea:
largestArea = area
largeContours.append(contour)
finalContour = contour
img = np.zeros((360,640,3))
cv2.drawContours(img, largeContours, -1, (255, 255, 255))
prevImg = img
if (prevImg is not img):
end_time = time.time() + 15
if time.time() > end_time:
epsilon = 0.1 * cv2.arcLength(finalContour, True)
approx = cv2.approxPolyDP(finalContour, epsilon, True)
print(approx)
prevImg = np.zeros((360,640,3))
cv2.drawContours(prevImg,[approx],0,(255,255,255),2)
cv2.imshow('final', prevImg)
video.release()
break
cv2.imshow('frame',frame)
cv2.imshow('img', img)
cv2.imshow('edges', edges)
if cv2.waitKey(33) == ord('q'):
video.release()
break
Small Rectangle
Frame, Edges, Contours
The 'img' tab shows the finalContour which is not of the maximum width and height

Colour Detection During Line Following

I am working on a line follower bot which follows black line on white background The code is something like this
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
try:
while(1):
## Read the image
ret, img = cap.read()
#cv2.imshow('image',img)
img = img[160:320,:,:]
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,170,255,cv2.THRESH_BINARY_INV)
#Applying Dilation Morphological Operation To Dilate the image
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
dilation = cv2.dilate(thresh, kernel, iterations = 5)
#Apllying Gaussian Bluring Algorithm to reduce the number of contours
blur3 = cv2.medianBlur (dilation,5)
i = 0
contours, hierarchy = cv2.findContours(blur3,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,contours,-1,(0,255,0),3)
cv2.imshow('image',img)
M = cv2.moments(contours[i])
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if cx < 190:
print "LEFT"
if cx > 450:
print "RIGHT "
if ((cx > 189) & (cx < 449)):
print "FRONT"
finally:
cap.release()
cv2.destroyAllWindows()
I want to also detect blue and red color while the bot is following the line using the same camera.
I tried reading center pixel of the centroid center_px = img[cy,cx]
But reading just one pixel dosent prove efficient in determining the color.
So is there any other approach to do this
I am using python2.7 with cv2

Categories