For each frame of the video, I am drawing a rectangle and a circle around the object that I am detecting. I want to save and draw all the shapes that were drawn throughout the loop onto the last frame of the video. Sort of like saving an image that has the trajectory of the object throughout the video. I don't know how to achieve this. Any advice would be appreciated.
import cv2
import numpy as np
# Open the video
cap = cv2.VideoCapture('v.mp4')
while(1):
success, frame = cap.read()
# Take each frame
if success:
crop_img = frame[200:2000, 400:2500].copy()
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1.5, minDist=505,param1=75, param2=30, minRadius=8, maxRadius=30)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
print(x,y)
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(crop_img, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(crop_img, (x - 5, y - 5),
(x + 5, y + 5), (0, 128, 255), -1)
cv2.imshow('hi', crop_img)
cv2.waitKey(0)
else:
break
cv2.destroyAllWindows()
import cv2
import numpy as np
# Open the video
cap = cv2.VideoCapture('0.mp4')
circles_all = []
I had added circles_all list to store all circles that you detected.
while True:
success, new_frame = cap.read()
# Take each frame
if success:
frame = new_frame
crop_img = frame[200:2000, 400:2500].copy()
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1.5, minDist=505, param1=75, param2=30, minRadius=8,
maxRadius=30)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for x, y, r in circles:
print(x, y)
circles_all.append([x, y, r])
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(crop_img, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(crop_img, (x - 5, y - 5),
(x + 5, y + 5), (0, 128, 255), -1)
cv2.imshow('hi', crop_img)
cv2.waitKey(0)
Then added circles_all.append to append coords and radius.
After that when no more frames, you iterate trought circles_all and save image.
else:
for (x, y, r) in circles_all:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(frame, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(frame, (x - 5, y - 5),
(x + 5, y + 5), (0, 128, 255), -1)
cv2.imwrite('lastframe.jpg', frame)
break
cv2.destroyAllWindows()
Final efect is like this for ten frames.
Related
i had added transparency to the whole image, but later i also added rectangle and aligned it by center. How to delete transparency in rectangle area?
Code:
import cv2
image = cv2.imread('test.jpeg')
overlay = image.copy()
print(image.shape)
x, y, w, h = 0, 0, image.shape[1], image.shape[0] # Rectangle parameters
cv2.rectangle(overlay, (x, y), (x+w, y+h), (500, 500, 500), -1) # A filled rectangle
Y, X = overlay.shape[0], overlay.shape[1]
print(X, Y)
cv2.rectangle(overlay, pt1=((X // 2) - 150,(Y // 2) - 150), pt2=((X // 2) + 150,(Y // 2) + 150), color=(0,0,0), thickness=5)
alpha = 0.5 # Transparency factor.
# Following line overlays transparent rectangle over the image
image_new = cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0)
cv2.imshow('frame', image_new)
cv2.waitKey(0) # waits until a key is pressed
cv2.destroyAllWindows()
You can simply index with the rectangle to copy the original pixels values into the new image:
import cv2
image = cv2.imread('test.jpeg')
overlay = image.copy()
print(image.shape)
x, y, w, h = 0, 0, image.shape[1], image.shape[0] # Rectangle parameters
cv2.rectangle(overlay, (x, y), (x+w, y+h), (500, 500, 500), -1) # A filled rectangle
Y, X = overlay.shape[0], overlay.shape[1]
# HERE: Save your points
pt1=((X // 2) - 150,(Y // 2) - 150)
pt2=((X // 2) + 150,(Y // 2) + 150)
cv2.rectangle(overlay, pt1=pt1, pt2=pt2, color=(0,0,0), thickness=5)
alpha = 0.5 # Transparency factor.
# Following line overlays transparent rectangle over the image
image_new = cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0)
# HERE: Put the pixels inside the rectangle back
image_new[pt1[1]:pt2[1], pt1[0]:pt2[0]] = image[pt1[1]:pt2[1], pt1[0]:pt2[0]]
cv2.imshow('frame', image_new)
cv2.waitKey(0) # waits until a key is pressed
cv2.destroyAllWindows()
So I have this image (480, 640, 3):
And I want to do detect different circles in it (mainly the red one, but you don't care).
Here is my code :
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('img0.png')
print(img.shape)
sat = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)[:, :, 1]
print(img.shape)
circles = circles = cv2.HoughCircles(sat, cv2.HOUGH_GRADIENT, 1, minDist=30, maxRadius=500)
print(circles)
if circles is not None: # code stolen from here : https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(img, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(img, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
plt.imshow(img)
plt.show()
Note that I use the saturation for the gray image, as I want the red part of my hand spinner, it is easier for me
Which output this :
Which is not that bad for me(ignore the wrong color scheme, It's bgr of opencv displayed as rgb), except that there is circles with too big radius. The output of print(circle) is :
[[[420.5 182.5 141.3]
[420.5 238.5 84.5]
[335.5 283.5 35. ]
[253.5 323.5 42.7]
[417.5 337.5 43.6]]]
(it is [x,y, radius])
Basically, it means that circles of interest has radius below 50, and I want to get rid of the two first one. I wanted to use the maxRadius parameter (notice that in my code, it is currently 500). So my guess was that if I set maxRadius at 50, it would remove the unwanted radius, but instead, it deleted all the circles... I have found that with maxRadius at 400, I got an output that "works":
And with maxRadius below 200, there is no more circles found.
What I am missing here ?
I am working on windows, python 3.7.7, last version of opencv
The following seems to work in Python/OpenCV.
Read the input
Convert to HSV and extract the saturation channel
Median filter
Do Hough Circles processing
Draw the circles
Save the results
Input
import cv2
import numpy as np
# Read image
img = cv2.imread('circles.png')
hh, ww = img.shape[:2]
# Convert to HSV and extract the saturation channel
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1]
# median filter
median = cv2.medianBlur(sat, 7)
# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(median, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=150, param2=50, minRadius=0, maxRadius=0)
print(circles)
# draw circles
result = img.copy()
for circle in circles[0]:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
(x,y,r) = circle
x = int(x)
y = int(y)
cv2.circle(result, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(result, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# save results
cv2.imwrite('circles_saturation.jpg', sat)
cv2.imwrite('circles_median.jpg', sat)
cv2.imwrite('circles_result.jpg', result)
# show images
cv2.imshow('sat', sat)
cv2.imshow('median', median)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Saturation image:
Median filtered image:
Results:
Circles data:
[[[258.5 323.5 52.7]
[340.5 193.5 51.3]
[422.5 326.5 34.1]
[333.5 276.5 33.6]]]
I try to recognize two areas in the following image. The area inside the inner and the area between the outer and inner - the border - circle with python openCV.
I tried different approaches like:
Detecting circles images using opencv hough circles
Find and draw contours using opencv python
That does not fit very well.
Is this even possible with classical image processing or do I need some neuronal networking?
Edit: Detecting circles images using opencv hough circles
# import the necessary packages
import numpy as np
import argparse
import cv2
from PIL import Image
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread(args["image"])
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 500)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# show the output image
img = Image.fromarray(image)
if img.height > 1500:
imS = cv2.resize(np.hstack([image, output]), (round((img.width * 2) / 3), round(img.height / 3)))
else:
imS = np.hstack([image, output])
# Resize image
cv2.imshow("gray", gray)
cv2.imshow("output", imS)
cv2.waitKey(0)
else:
print("No circle detected")
Testimage:
General mistake: While using HoughCircles() , the parameters should be chosen appropriately. I see that you are only using first 4 parameters in your code. Ypu can check here to get a good idea about those parameters.
Experienced idea: While using HoughCircles , I noticed that if 2 centers of 2 circles are same or almost close to each other, HoughCircles cant detect them. Even if you assign min_dist parameter to a small value. In your case, the center of circles also same.
My suggestion: I will attach the appropriate parameters with the code for both circles. I couldnt find 2 circles with one parameter list because of the problem I explained above. My suggestion is that apply these two parameters double time for the same image and just get the circles and get the result.
For outer circle result and parameters included code:
Result:
# import the necessary packages
import numpy as np
import argparse
import cv2
from PIL import Image
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread('image.jpg')
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray,15)
rows = gray.shape[0]
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT,1, rows / 8,
param1=100, param2=30,
minRadius=200, maxRadius=260)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# show the output image
img = Image.fromarray(image)
if img.height > 1500:
imS = cv2.resize(np.hstack([image, output]), (round((img.width * 2) / 3), round(img.height / 3)))
else:
imS = np.hstack([image, output])
# Resize image
cv2.imshow("gray", gray)
cv2.imshow("output", imS)
cv2.waitKey(0)
else:
print("No circle detected")
For inner circle the parameters:
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT,1, rows / 8,
param1=100, param2=30,
minRadius=100, maxRadius=200)
Result:
Extract Circular ROI & Show Radius of the Circle in Tkinter Label
I am requesting help from python experts in this community. I have searched about my problem all over Stackexchange as well as the Github community. But I didn't find anything helpful.
I have created a Tkinter GUI. In this GUI, I can upload my image from the destination folder. In Select of the evaluation section, I have written a script through which I can automatically view my ROI region in the circular part. The GUI is displayed at the bottom part of this question.
Help required Section: I am having trouble in creating a script through which:
when I click on Upload ROI button, only the selected ROI portion
of the image gets saved at the destination folder i.e path =
'Data/images/' + name + '_' + method + ext
I can view the Radius of the circle somewhere on the the Tkinter GUI.
def ROI(self, image, method):
if method == 'ROI':
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
blimage = cv2.medianBlur(image, 15)
circles = cv2.HoughCircles(blimage, cv2.HOUGH_GRADIENT, 1, 255, param1=100, param2=60, minRadius=0,
maxRadius=0)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
cv2.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 6)
cv2.circle(image, (i[0], i[1]), 2, (0, 0, 255), 3)
cv2.waitKey()
else:
print('method is wrong')
return image
GUI
UPDATE:
I added variable border to calculate x1,y1,x2,y2 so now it crops with borderline. Images show results for old code without border.
If you have only one circle (x,y,r) then you can use it to crop image
image = image[y-r:y+r, x-r:x+r]
I test it on some image with circle bigger then image and I had to use int16 instead of unit16 to get -1 instead of 65535 for 170-171 (y-r). Add I had to use min(), max()to get0instead-1`
def ROI(self, image, method):
if method == 'ROI':
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
blimage = cv2.medianBlur(image, 15)
circles = cv2.HoughCircles(blimage, cv2.HOUGH_GRADIENT, 1, 255, param1=100, param2=60, minRadius=0,
maxRadius=0)
if circles is not None:
#print(circles)
# need `int` instead of `uint` to correctly calculate `y-r` (to get `-1` instead of `65535`)
circles = np.int16(np.around(circles))
for x,y,r in circles[0, :]:
print('x, y, r:', x, y, r)
border = 6
cv2.circle(image, (x, y), r, (0, 255, 0), border)
cv2.circle(image, (x, y), 2, (0, 0, 255), 3)
height, width = image.shape
print('height, width:', height, width)
# calculate region to crop
x1 = max(x-r - border//2, 0) # eventually -(border//2+1)
x2 = min(x+r + border//2, width) # eventually +(border//2+1)
y1 = max(y-r - border//2, 0) # eventually -(border//2+1)
y2 = min(y+r + border//2, height) # eventually +(border//2+1)
print('x1, x2:', x1, x2)
print('y1, y2:', y1, y2)
# crop image
image = image[y1:y2,x1:x2]
print('height, width:', image.shape)
else:
print('method is wrong')
return image
For more circles you would have to first calculate region used for all circles (get drom all circles minimal values x-r,y-r and maximal values x+r,y+r) and next crop image.
Later I will try to use alpha channel to remove backgroud outside circle.
Image used for test (if someone else would like to test code)
EDIT: I added code which create black image with white circle to remove background.
def ROI(self, image, method):
if method == 'ROI':
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
blimage = cv2.medianBlur(image, 15)
circles = cv2.HoughCircles(blimage, cv2.HOUGH_GRADIENT, 1, 255, param1=100, param2=60, minRadius=0,
maxRadius=0)
if circles is not None:
print(circles)
circles = np.int16(np.around(circles)) # need int instead of uint to correctly calculate y-r (to get -1 instead of 65535)
for x,y,r in circles[0, :]:
print('x, y, r:', x, y, r)
height, width = image.shape
print('height, width:', height, width)
border = 6
cv2.circle(image, (x, y), r, (0, 255, 0), border)
cv2.circle(image, (x, y), 2, (0, 0, 255), 3)
mask = np.zeros(image.shape, np.uint8) # black background
cv2.circle(mask, (x, y), r, (255), border) # white mask for black border
cv2.circle(mask, (x, y), r, (255), -1) # white mask for (filled) circle
#image = cv2.bitwise_and(image, mask) # image with black background
image = cv2.bitwise_or(image, ~mask) # image with white background
x1 = max(x-r - border//2, 0) # eventually -(border//2+1)
x2 = min(x+r + border//2, width) # eventually +(border//2+1)
y1 = max(y-r - border//2, 0) # eventually -(border//2+1)
y2 = min(y+r + border//2, height) # eventually +(border//2+1)
print('x1, x2:', x1, x2)
print('y1, y2:', y1, y2)
image = image[y1:y2,x1:x2]
print('height, width:', image.shape)
else:
print('method is wrong')
return image
I'm trying to detect this black circle here. Shouldn't be too difficult but for some reason I just get 0 circles or approximately 500 circles everywhere, depending on the arguments. But there is no middle ground. Feels like I have tried to play with the arguments for hours, but absolutely no success. Is there a problem using HoughCircles and black or white picture? The task seems simple to a human eye, but is this difficult to the computer for some reason?
Here's my code:
import numpy as np
import cv2
image = cv2.imread('temp.png')
output = image.copy()
blurred = cv2.blur(image,(10,10))
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.5, 20, 100, 600, 10, 100)
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
print len(circles)
for (x, y, r) in circles:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
show the output image
cv2.imshow("output", np.hstack([output]))
cv2.waitKey(0)
There are few minor mistakes in your approach.
Here is the code I used from the documentation:
img = cv2.imread('temp.png',0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
cimg1 = cimg.copy()
circles = cv2.HoughCircles img,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,255,255),3)
cv2.imshow('detected circles.jpg',cimg)
joint = np.hstack([cimg1, cimg]) #---Posting the original image along with the image having the detected circle
cv2.imshow('detected circle and output', joint )