Related
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.
This is the first time I am trying out YOLov3. I am trying to obtain the cropped image within the bounding box predicted by YOLOv3. I am able to obtain the cropped image. The problem is, the image is of a different colour than the original cropped image. I want to pass this cropped image into another model so that I can confirm that the cropped image does have fire in it.
I can't do that when the cropped image is different from the original. Any help would be appreciated.
Here is the piece of code for cropping the image:
import cv2
import numpy as np
import glob
import random
from scipy import ndimage
import matplotlib.pyplot as plt
from PIL import Image
# Load Yolo
net = cv2.dnn.readNet("D:\obj_detection\yolo_custom_detection\yolov3_training_last.weights", "D:\obj_detection\yolo_custom_detection\yolov3_testing.cfg")
# Name custom object
classes = ["fire"]
# Images path
images_path = glob.glob(r"D:\obj_detection\test_img\*.jpg")
layer_names = net.getLayerNames() #Get the name of all layers of the network.
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()] #Get the index of the output layers.
#These two functions are used for getting the output layers (82,94,106).
colors = np.random.uniform(0, 255, size=(len(classes), 3))
# Insert here the path of your images
random.shuffle(images_path)
# loop through all the images
for img_path in images_path:
# Loading image
img = cv2.imread(img_path)
img = cv2.resize(img, None, fx=0.4, fy=0.4)
height, width, channels = img.shape
# Detecting objects
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
# Syntax: blob = cv2.dnn.blobFromImage(image, scalefactor=1.0, size, mean, swapRB=True)
net.setInput(blob)
outs = net.forward(output_layers)
# Showing informations on the screen
class_ids = []
confidences = []
boxes = []
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.3:
# Object detected
print(class_id)
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# Rectangle coordinates
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4) #Non max suppression
print(indexes)
font = cv2.FONT_HERSHEY_COMPLEX_SMALL
for i in range(len(boxes)):
if i in indexes:
x, y, w, h = boxes[i]
if x<0:
x = 0
if y<0:
y = 0
if w<0:
w = 0
if h<0:
h = 0
print("The box dimensions:",boxes[i])
print(img_path)
label = str(classes[class_ids[i]])
color = colors[class_ids[i]]
print("Dimensions of x:", x,",y:",y,",x+w:", x+w, ",y+h:", y+h)
cropped_image = img[y:y+h, x:x+w]
plt.imshow(cropped_image)
cv2.rectangle(img, (x, y), (x + w, y + h), color, 1)
cv2.putText(img, label, (x, y + 30), font, 0.8, color, 2)
img = cv2.resize(img, (700, 600))
cv2.imshow("Fire Detection", img)
cv2.resizeWindow("Fire Detection", 1600,1200)
key = cv2.waitKey(0)
if key == ord('q'):
break
cv2.destroyAllWindows()
The output I obtain is:
Original Image:
enter image description here
Cropped image:
enter image description here
How can I obtain the cropped image as it is in the original image (the same colour)?
I have a project of opencv where on the frame I am displaying some text using cv2.putText(). Currently it looks like below:
As you can see on the top left corner, the text is present but its not clearly visible. Is it possible to make background black so that the text will then appear good. Something like below image:
Even if the black background covers till right side of the frame, that is also fine. Below is the code I am using for putting text on frame:
cv2.putText(frame, "Data: N/A", (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
cv2.putText(frame, "Room: C1", (5, 60), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
Is there any prebuilt method/library available in opencv which can do this. Can anyone please suggest a good way?
Use this function:
import cv2
def draw_text(img, text,
font=cv2.FONT_HERSHEY_PLAIN,
pos=(0, 0),
font_scale=3,
font_thickness=2,
text_color=(0, 255, 0),
text_color_bg=(0, 0, 0)
):
x, y = pos
text_size, _ = cv2.getTextSize(text, font, font_scale, font_thickness)
text_w, text_h = text_size
cv2.rectangle(img, pos, (x + text_w, y + text_h), text_color_bg, -1)
cv2.putText(img, text, (x, y + text_h + font_scale - 1), font, font_scale, text_color, font_thickness)
return text_size
Then you can invoke the function like this:
image = 127 * np.ones((100, 200, 3), dtype="uint8")
pos = (10, 10)
w, h = draw_text(image, "hello", pos=(10, 10))
draw_text(image, "world", font_scale=4, pos=(10, 20 + h), text_color_bg=(255, 0, 0))
cv2.imshow("image", image)
cv2.waitKey()
note that by default it paints a black background, but you can use a different color if you want.
There's no prebuilt method but a simple appraoch is to use cv2.rectangle + cv2.putText. All you need to do is to draw the black rectangle on the image followed by placing the text. You can adjust the x,y,w,h parameters depending on how large/small you want the rectangle. Here's an example:
Input image:
Result:
import cv2
import numpy as np
# Load image, define rectangle bounds
image = cv2.imread('1.jpg')
x,y,w,h = 0,0,175,75
# Draw black background rectangle
cv2.rectangle(image, (x, x), (x + w, y + h), (0,0,0), -1)
# Add text
cv2.putText(image, "THICC flower", (x + int(w/10),y + int(h/2)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2)
# Display
cv2.imshow('image', image)
cv2.waitKey()
Here is one way to do that in Python OpenCV.
Read the input
Create an image of your desired background color that is the same size as the input
Draw your text on the background image
Get the bounding rectangle for the text region
Copy the text region from the background color image to a copy of the input image
Save the results
Input:
import cv2
import numpy as np
# load image
img = cv2.imread("zelda1.jpg")
# create same size image of background color
bg_color = (0,0,0)
bg = np.full((img.shape), bg_color, dtype=np.uint8)
# draw text on bg
text_color = (0,0,255)
cv2.putText(bg, "Data: N/A", (5,30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 0.75, text_color, 1)
# get bounding box
# use channel corresponding to color so that text is white on black background
x,y,w,h = cv2.boundingRect(bg[:,:,2])
print(x,y,w,h)
# copy bounding box region from bg to img
result = img.copy()
result[y:y+h, x:x+w] = bg[y:y+h, x:x+w]
# write result to disk
cv2.imwrite("zelda1_background_text.jpg", bg)
cv2.imwrite("zelda1_text.jpg", result)
# display results
cv2.imshow("TEXT", bg)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Text on background color image:
Text on input image:
P.S. You can adjust the bounding rectangle (x,y,w,h) values to add some padding if you want when you do the crop.
import cv2 \
import numpy as np
#### Load image, define rectangle bounds
image = cv2.imread(r'C:\Users\Bharath\Downloads\test.jpg')
#### overlay space
x,y,w,h = 40,30,300,60
#### alpha, the 4th channel of the image
alpha = 0.3
overlay = image.copy()
output = image.copy()
##### corner
cv2.rectangle(overlay, (x, x), (x + w, y + h), (0,0,0), -1)
##### putText
cv2.putText(overlay, "HELLO WORLD..!", (x + int(w/10),y + int(h/1.5)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2)
#### apply the overlay
cv2.addWeighted(overlay, alpha, output, 1 - alpha,0, output)
##### Display
cv2.imshow("Output", output)\
cv2.waitKey(0)
`
input
output
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 am using OpenCV's HOG detector to detect pedestrians in a video. But the bounding boxes returned by it's detectMultiScale() have one negative value. I couldn't find anything helpful or useful so far on internet to understand and solve this problem. I do not even know why is this problem occurring. that's the output.
RECTS: [[183 -6 68 137]
[ 76 -7 76 152]]
WEIGHTS: [[ 1.21099767]
[ 0.37004868]]
Heres my code:
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
webcam = cv2.VideoCapture ('/home/irum/Desktop/Test-Videos/pedistrianTestVideoLONG.mp4')
while True:
# read each frame
ret, frame = webcam.read()
# resize it
image = imutils.resize(frame, width=min(300, frame.shape[1]))
orig = image.copy()
# detect people in the frame
(rects, weights) = hog.detectMultiScale(image, winStride=(4, 4),
padding=(8, 8), scale=1.1)
print('RECTS: ',rects)
print('WEIGHTS: ',weights)
print('LENGTH: ',len(rects))
# draw the original bounding boxes
#for (x, y, w, h) in rects:
for i in range(len(rects)):
body_i = rects[i]
print('BODY_I: ',body_i)
(x, y, w, h) = [v * 1 for v in body_i]
print ('DETECTION (x, y, w, h)',x, y, w, h)
cv2.rectangle(orig, (x, y), (x + w, y + h), (0, 0, 255), 2)
# apply non-maxima suppression
rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
pick = non_max_suppression(rects, probs=None, overlapThresh=0.65)
# draw the final bounding boxes
for i in range(len(pick)):
g += 1
body_p = pick[i]
(xA, yA, xB, yB) = [int(v * 1) for v in body_p]
print('DETECTION NMS (xA, yA, xB, yB)', xA, yA, xB, yB)
# rect on scaled image
cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2)
# rects to map on original frame
(x1, y1, w1, h1) = [int(v * 4.28) for v in body_p]
print('(x1, y1, w1, h1) ' ,x1, y1, w1, h1)
cv2.rectangle(frame, (x1, y1), (w1, h1), (0, 45, 255), 2)
# Crop body from Original frame
body_big = frame[y1:h1, x1:w1]
print('DISPLAY')
cv2.imshow("BODY", body_big)
# Save body
save_body_path = '/home/irum/Desktop/pedestrian-detection/BIG_BODY'
cur_date = (time.strftime("%Y-%m-%d"))
cur_time = (time.strftime("%H:%M:%S"))
new_pin =cur_date+"-"+cur_time
filename1 = 'BIG'
filename2 = str(g)+str(filename1)+'-'+str(new_pin)
#print ("IMAGE TO SEND: ",filename2)
sampleFile = ('%s/%s.jpg' % (save_body_path, filename2))
#print ("sampleFile",sampleFile)
cv2.imwrite('%s/%s.jpg' % (save_body_path, filename2), body_big)
#pyplot.imsave('%s.jpg' % (sampleFile), body_big)
# show the output images
cv2.imshow("Before NMS", orig)
cv2.imshow("After NMS", image)
cv2.imshow("BIG BODY", frame)
# cv2.imshow("FACE", body_big2)
key = cv2.waitKey(10)
if key == 27:
break
After trying almost everything on internet when I couldn't find answer anywhere I started analyzing this problem myself and found the solution to it.
(rects, weights) = hog.detectMultiScale(image, winStride=(4, 4),
padding=(0, 0), scale=1.1)
I change the argument padding value to (0,0) and it solved my problem. And I think the problem was because when the body was detected in the corner of the frame due to padding given , it wanted to take some space from the sides of frame while cropping but there wasn't any space available so it was giving me negatives.