Here's a picture of a maze solver, when the BFS function goes over the image it goes around the image
I know it's possible to just crop the image manually, but I want a function where the computer can automatically detect non black edges and delete them. How would I do this?
#Program that will read an image and pass through to folder using OpenCV, select starting and ending points, and solve the maze using a BFS function
#will be imported on to flask backend
'''
Requirements: pip3 install opencv-python (or contrib version depending on console)
'''
#libraries
import imghdr
import cv2
import numpy as np
import threading
import colorsys
#first we will read the image, and countour it to a gray image so reading the image will be easier
#set threshhold value
img = cv2.imread("Mazes/maze3.jpg", cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY)
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
h, w = img.shape[:2]
#seperate function to display image
def solution():
global img
#show image
cv2.imshow("Maze Solver", img)
#run mouse pointer function for user to click points
cv2.setMouseCallback('Maze Solver', maze_points)
while True:
#after thread is ran display image
cv2.imshow("Maze Solver", img)
cv2.waitKey(1)
#this is a function that will map the cursor as a point so user can press twice to determine the start and end points
class Point(object):
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
#variable that will store the # of times mouse is clicked
num_of_clicks = 0
#determine how big pointer will appear on screen
mouse_click_size = 2
#initialize start and end pointer so user clicks
start_point = Point()
end_point = Point()
#subtree that will point nodes in directions that they go in
subtree = [Point(0, -1), Point(0, 1), Point(1, 0), Point(-1, 0)]
#this function is to call the mouse to click two points on the image (just for opencv, if needed can input through console)
def maze_points(running, pX, pY, flags, param):
#globals
global img, start_point, end_point, num_of_clicks
#if the running is true then run
if running == cv2.EVENT_LBUTTONUP:
#start point clikc
if num_of_clicks == 0:
cv2.rectangle(img, (pX - mouse_click_size, pY - mouse_click_size),
(pX + mouse_click_size, pY + mouse_click_size), (0, 0, 255), -1)
start_point = Point(pX, pY)
print("start = ", start_point.x, start_point.y)
num_of_clicks += 1
#end point click
elif num_of_clicks == 1:
cv2.rectangle(img, (pX - mouse_click_size, pY - mouse_click_size),
(pX + mouse_click_size, pY + mouse_click_size), (0, 200, 50), -1)
end_point = Point(pX, pY)
print("end = ", end_point.x, end_point.y)
num_of_clicks += 1
#this is the bfs function that will search through all nodes and cells using bfs
def solve_maze(start, end):
#globals
global img, h, w
const = 10000
#if a path is found display (debugging)
true_path = False
#set queue
queue = []
#if cell has been checked for valid path
cell_checked = [[0 for j in range(w)] for i in range(h)]
#parent subtree for checked cells
tree_begin = [[Point() for j in range(w)] for i in range(h)]
#store starting point for maze
queue.append(start)
cell_checked[start.y][start.x] = 1
#loop to search through nodes until maze is empty
while len(queue) > 0:
valid_paths = queue.pop(0)
#will search through subtree by surrounding cells
for nodes in subtree:
cell = valid_paths + nodes
point_x = cell.x
point_y = cell.y
solution_max = 0
#to solve this we will determine all borders as black and search through them
# if cell(a surrounding pixel) is in range of image, not visited, !(B==0 && G==0 && R==0) i.e. pixel is
# not black as black represents border
if (point_x >= 0 and point_x < w and point_y >= 0 and point_y < h and cell_checked[point_y][point_x] == solution_max and
(img[point_y][point_x][0] != 0 or img[point_y][point_x][1] != 0 or img[point_y][point_x][2] != 0)):
queue.append(cell)
cell_checked[cell.y][cell.x] = cell_checked[valid_paths.y][valid_paths.x] + 1 # Later
#bfs function characteristics (blue)
img[cell.y][cell.x] = list(reversed(
[i * 255 for i in colorsys.rgb_to_hsv(cell_checked[cell.y][cell.x] / const, 1, 1)])
)
#once tree path is found
tree_begin[cell.y][cell.x] = valid_paths
#end path once end pixel is found
if cell == end:
true_path = True
del queue[:]
break
#list of paths to trace it
path_nodes = []
#display the found path
if true_path:
valid_paths = end
#loop to get path from found correct path from bfs, end to start, and reverse it to display path from start
while valid_paths != start:
path_nodes.append(valid_paths)
valid_paths = tree_begin[valid_paths.y][valid_paths.x]
path_nodes.append(valid_paths)
path_nodes.reverse()
#display path
for valid_paths in path_nodes:
img[valid_paths.y][valid_paths.x] = [0, 0, 255]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
#p = cv2.dilate(img[p.y], kernel, iterations=2)
#console display
print("Path Found")
else:
print("Path Not Found")
#console function for points
print("Select start and end points : ")
#use a thread function to simoultaneously run display function, mouse function, and run BFS through it
mazeSolver = threading.Thread(target=solution, args=())
#daemon will join any other exisitng thread
mazeSolver.daemon = True
mazeSolver.start()
#when clicks are less than 2 don't run any functions
while num_of_clicks < 2:
pass
#solve maze
solve_maze(start_point, end_point)
cv2.waitKey(0)
'''
50 images tested with 93% accuracy rate
'''
I'm using win10 and latest version of OpenCV.
Related
I have prepared some code for it to lock when I get up from computer and get away from it
but when I use it with a casual algorithm, it turns off immediately because it does not detect my face in some movements. For this, I want it to wait 3 seconds when it does not detect my face, check it again and if it still does not detect my face, I want it to lock but when I use the time.sleep method, webcam video freezes and works as face does not exist even my face at camera, what kind of working algorithm do you suggest for this?
from multiprocessing.connection import wait
import cv2
import time
import pyautogui
import ctypes
from math import sin, cos, radians
camera = cv2.VideoCapture(0)
face = cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")
settings = {
'scaleFactor': 1.3,
'minNeighbors': 3,
'minSize': (50, 50),
'flags': cv2.CASCADE_FIND_BIGGEST_OBJECT|cv2.CASCADE_DO_ROUGH_SEARCH
}
def rotate_image(image, angle):
if angle == 0: return image
height, width = image.shape[:2]
rot_mat = cv2.getRotationMatrix2D((width/2, height/2), angle, 0.9)
result = cv2.warpAffine(image, rot_mat, (width, height), flags=cv2.INTER_LINEAR)
return result
def rotate_point(pos, img, angle):
if angle == 0: return pos
x = pos[0] - img.shape[1]*0.4
y = pos[1] - img.shape[0]*0.4
newx = x*cos(radians(angle)) + y*sin(radians(angle)) + img.shape[1]*0.4
newy = -x*sin(radians(angle)) + y*cos(radians(angle)) + img.shape[0]*0.4
return int(newx), int(newy), pos[2], pos[3]
while True:
ret, img = camera.read()
for angle in [0, -25, 25]:
rimg = rotate_image(img, angle)
detected = face.detectMultiScale(rimg, **settings)
if len(detected):
detected = [rotate_point(detected[-1], img, -angle)]
break
for x, y, w, h in detected[-1:]:
cv2.rectangle(img, (x, y), (x+w, y+h), (255,0,0), 2)
cv2.imshow('facedetect', img)
if cv2.waitKey(5) != -1:
break
if 0==(len(detected)):
time.sleep(3)
if 1==(len(detected)):
pass
else:
ctypes.windll.user32.LockWorkStation()
cv2.destroyWindow("facedetect")```
set a variable with the last timestamp where you wouldn't have detected a face. On every loop, if you detect your face again, set this variable to None, if this variable is not None and variable + 3secondes <= current timestamp, lock your station.
import time
unseen_from = None
while True:
# etc etc
detected = bool(detected) # empty list == False, True otherwise
if unseen_from is None:
detected = None if detected else time.time()
elif detected:
unseen_from = None
else if detected_from + 3 < time.time():
ctypes.windll.user32.LockWorkStation()
live coding, I don't have a windows to test this on, but the idea is there
For some reason while I run OpenCV, when it detects a positive image instantly the detector processing time goes up ridiculously. Below in the code I labeled where the time is printed. Normally that output is about a tenth of a second but one it detects a positive image it instantly goes to 50 seconds and more! What causes this?
Note: This project is being done on a raspberry pi zero.
import picamera
from picamera.array import PiRGBArray
import cv2
import time
import numpy as np
import logging
image_path = '/home/pi/photo.bgr'
def DetectWeeds(img):
time1 = time.time()
weedDetector = cv2.CascadeClassifier('/home/pi/WeedClassifier/data/cascade.xml')
found = weedDetector.detectMultiScale(img, minSize=(20, 20))
time2 = time.time()
print(time2 - time1) #PROCESSING TIMER HERE
amount_found = len(found)
i = 0
time.sleep(.1)
#Taking a lot of time when a weed is detected
if amount_found != 0:
# There may be more than one
# sign in the image
firstset = found[0]
x, y, width, height = firstset
imagewidth = 640
currentx = (x + width) / 2 + x
currenty = (y + height) / 2 + y
targetx = imagewidth / 2
return currentx, targetx, currenty
if amount_found == 0:
return 0, -1, 0
def main():
camera = picamera.PiCamera()
camera.resolution = (640, 480)
camera.framerate = 16
rawCapture = PiRGBArray(camera, size=(640, 480))
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
image = frame.array
currentx, targetx, currenty = DetectWeeds(image)
if currentx != 0:
if currentx > targetx:
print("Move Left!")
if currentx < targetx:
print("Move Right!")
if currentx == targetx:
print("Centered!")
print("Move Forward Now!")
if currentx == 0 and targetx == -1:
print("No Weeds Here")
rawCapture.truncate(0)
time.sleep(.08)
main()
I would suggest improving the code as below and see if you get any improvement in time. It can also be because when no weed is detected, the code is doing nothing and when weed is detected, the code has to run few more lines of code.
def DetectWeeds(img):
time1 = time.time()
weedDetector = cv2.CascadeClassifier('/home/pi/WeedClassifier/data/cascade.xml')
found = weedDetector.detectMultiScale(img, minSize=(20, 20))
time2 = time.time()
print(time2 - time1) #PROCESSING TIMER HERE
amount_found = len(found)
i = 0
time.sleep(.1)
#Taking a lot of time when a weed is detected
if amount_found != 0:
# There may be more than one
# sign in the image
x, y, width, height = found[0]
currentx = (x + width) / 2 + x
currenty = (y + height) / 2 + y
targetx = 640 / 2
return currentx, targetx, currenty
if amount_found == 0:
return 0, -1, 0
found = weedDetector.detectMultiScale(img, minSize=(20, 20))
The problem is most likely lies in the scaleFactor parameter passed in detectMultiscale() function.
scaleFactor: Parameter specifying how much the image size is reduced at each image scale.
Tweak the scaleFactor in the detectMultiscale() function with multiple settings.
Increasing the scale factor increases the accuracy and is also computationally expensive. The Good thumb rule is to find the right sweet spot where it runs in a reasonable time at sufficient accuracy.
If still if it doesn't work then try to adjust other parameters like maxSize,minNeighbors.
I am trying to segment a web page into a header, footer, left panel, right panel, etc. (get coordinates) using an image processing tool(OpenCV), which is not giving satisfactory results.
I want to get something like this:
But, all I got
import cv2
import numpy
from google.colab.patches import cv2_imshow
img = cv2.imread("test.png")
blue, green, red = cv2.split(img)
def medianCanny(img, thresh1, thresh2):
median = numpy.median(img)
img = cv2.Canny(img, int(thresh1 * median), int(thresh2 * median))
return img
blue_edges = medianCanny(blue, 0, 1)
green_edges = medianCanny(green, 0, 1)
red_edges = medianCanny(red, 0, 1)
edges = blue_edges | green_edges | red_edges
contours,hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL ,cv2.CHAIN_APPROX_SIMPLE)
hierarchy = hierarchy[0]
for component in zip(contours, hierarchy):
currentContour = component[0]
currentHierarchy = component[1]
x,y,w,h = cv2.boundingRect(currentContour)
if currentHierarchy[3] < 0:
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)
cv2_imshow(img)
cv2.waitKey(0)
cv2.destroyAllWindows()
And I want to connect nearby boxes, but I don't understand how best to do it.
Test image
I started with your code as a base to get the green rectangles. I filtered the boxes by size to get rid of the big ones that contained large chunks of the image (there's even one that goes around the entire image). From there I iteratively merged nearby boxes until there were no more overlapping boxes. I used the merge_margin variable to set how close two boxes needed to be before they counted as "overlapping".
Each Step (I highlighted the last merged box and the points it found inside)
(This gif is heavily compressed so you'll see some artifacting)
Final Image
import cv2
import numpy as np
# tuplify
def tup(point):
return (point[0], point[1]);
# returns true if the two boxes overlap
def overlap(source, target):
# unpack points
tl1, br1 = source;
tl2, br2 = target;
# checks
if (tl1[0] >= br2[0] or tl2[0] >= br1[0]):
return False;
if (tl1[1] >= br2[1] or tl2[1] >= br1[1]):
return False;
return True;
# returns all overlapping boxes
def getAllOverlaps(boxes, bounds, index):
overlaps = [];
for a in range(len(boxes)):
if a != index:
if overlap(bounds, boxes[a]):
overlaps.append(a);
return overlaps;
img = cv2.imread("test.png")
orig = np.copy(img);
blue, green, red = cv2.split(img)
def medianCanny(img, thresh1, thresh2):
median = np.median(img)
img = cv2.Canny(img, int(thresh1 * median), int(thresh2 * median))
return img
blue_edges = medianCanny(blue, 0, 1)
green_edges = medianCanny(green, 0, 1)
red_edges = medianCanny(red, 0, 1)
edges = blue_edges | green_edges | red_edges
# I'm using OpenCV 3.4. This returns (contours, hierarchy) in OpenCV 2 and 4
_, contours,hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL ,cv2.CHAIN_APPROX_SIMPLE)
# go through the contours and save the box edges
boxes = []; # each element is [[top-left], [bottom-right]];
hierarchy = hierarchy[0]
for component in zip(contours, hierarchy):
currentContour = component[0]
currentHierarchy = component[1]
x,y,w,h = cv2.boundingRect(currentContour)
if currentHierarchy[3] < 0:
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)
boxes.append([[x,y], [x+w, y+h]]);
# filter out excessively large boxes
filtered = [];
max_area = 30000;
for box in boxes:
w = box[1][0] - box[0][0];
h = box[1][1] - box[0][1];
if w*h < max_area:
filtered.append(box);
boxes = filtered;
# go through the boxes and start merging
merge_margin = 20;
# this is gonna take a long time
finished = False;
highlight = [[0,0], [1,1]];
points = [[[0,0]]];
while not finished:
# set end con
finished = True;
# check progress
print("Len Boxes: " + str(len(boxes)));
# draw boxes # comment this section out to run faster
copy = np.copy(orig);
for box in boxes:
cv2.rectangle(copy, tup(box[0]), tup(box[1]), (0,200,0), 1);
cv2.rectangle(copy, tup(highlight[0]), tup(highlight[1]), (0,0,255), 2);
for point in points:
point = point[0];
cv2.circle(copy, tup(point), 4, (255,0,0), -1);
cv2.imshow("Copy", copy);
key = cv2.waitKey(1);
if key == ord('q'):
break;
# loop through boxes
index = 0;
while index < len(boxes):
# grab current box
curr = boxes[index];
# add margin
tl = curr[0][:];
br = curr[1][:];
tl[0] -= merge_margin;
tl[1] -= merge_margin;
br[0] += merge_margin;
br[1] += merge_margin;
# get matching boxes
overlaps = getAllOverlaps(boxes, [tl, br], index);
# check if empty
if len(overlaps) > 0:
# combine boxes
# convert to a contour
con = [];
overlaps.append(index);
for ind in overlaps:
tl, br = boxes[ind];
con.append([tl]);
con.append([br]);
con = np.array(con);
# get bounding rect
x,y,w,h = cv2.boundingRect(con);
# stop growing
w -= 1;
h -= 1;
merged = [[x,y], [x+w, y+h]];
# highlights
highlight = merged[:];
points = con;
# remove boxes from list
overlaps.sort(reverse = True);
for ind in overlaps:
del boxes[ind];
boxes.append(merged);
# set flag
finished = False;
break;
# increment
index += 1;
cv2.destroyAllWindows();
# show final
copy = np.copy(orig);
for box in boxes:
cv2.rectangle(copy, tup(box[0]), tup(box[1]), (0,200,0), 1);
cv2.imshow("Final", copy);
cv2.waitKey(0);
Edit: The inefficiency of this bothered me a bit. The order that the boxes gets merged in doesn't really make sense. You can see that there are a lot of steps where little boxes are merging into a big box, rather than a big box eating everything inside of itself and growing. Turns out this was a really easy code fix. Since the new merged boxes are appended to the end of the boxes list, we can just index in reverse to make it so that we go from big to small.
I changed the merge_margin to 15 since I think that's closer to the target solution in the question.
import cv2
import numpy as np
# tuplify
def tup(point):
return (point[0], point[1]);
# returns true if the two boxes overlap
def overlap(source, target):
# unpack points
tl1, br1 = source;
tl2, br2 = target;
# checks
if (tl1[0] >= br2[0] or tl2[0] >= br1[0]):
return False;
if (tl1[1] >= br2[1] or tl2[1] >= br1[1]):
return False;
return True;
# returns all overlapping boxes
def getAllOverlaps(boxes, bounds, index):
overlaps = [];
for a in range(len(boxes)):
if a != index:
if overlap(bounds, boxes[a]):
overlaps.append(a);
return overlaps;
img = cv2.imread("test.png")
orig = np.copy(img);
blue, green, red = cv2.split(img)
def medianCanny(img, thresh1, thresh2):
median = np.median(img)
img = cv2.Canny(img, int(thresh1 * median), int(thresh2 * median))
return img
blue_edges = medianCanny(blue, 0, 1)
green_edges = medianCanny(green, 0, 1)
red_edges = medianCanny(red, 0, 1)
edges = blue_edges | green_edges | red_edges
# I'm using OpenCV 3.4. This returns (contours, hierarchy) in OpenCV 2 and 4
_, contours,hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL ,cv2.CHAIN_APPROX_SIMPLE)
# go through the contours and save the box edges
boxes = []; # each element is [[top-left], [bottom-right]];
hierarchy = hierarchy[0]
for component in zip(contours, hierarchy):
currentContour = component[0]
currentHierarchy = component[1]
x,y,w,h = cv2.boundingRect(currentContour)
if currentHierarchy[3] < 0:
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)
boxes.append([[x,y], [x+w, y+h]]);
# filter out excessively large boxes
filtered = [];
max_area = 30000;
for box in boxes:
w = box[1][0] - box[0][0];
h = box[1][1] - box[0][1];
if w*h < max_area:
filtered.append(box);
boxes = filtered;
# go through the boxes and start merging
merge_margin = 15;
# this is gonna take a long time
finished = False;
highlight = [[0,0], [1,1]];
points = [[[0,0]]];
while not finished:
# set end con
finished = True;
# check progress
print("Len Boxes: " + str(len(boxes)));
# draw boxes # comment this section out to run faster
copy = np.copy(orig);
for box in boxes:
cv2.rectangle(copy, tup(box[0]), tup(box[1]), (0,200,0), 1);
cv2.rectangle(copy, tup(highlight[0]), tup(highlight[1]), (0,0,255), 2);
for point in points:
point = point[0];
cv2.circle(copy, tup(point), 4, (255,0,0), -1);
cv2.imshow("Copy", copy);
key = cv2.waitKey(1);
if key == ord('q'):
break;
# loop through boxes
index = len(boxes) - 1;
while index >= 0:
# grab current box
curr = boxes[index];
# add margin
tl = curr[0][:];
br = curr[1][:];
tl[0] -= merge_margin;
tl[1] -= merge_margin;
br[0] += merge_margin;
br[1] += merge_margin;
# get matching boxes
overlaps = getAllOverlaps(boxes, [tl, br], index);
# check if empty
if len(overlaps) > 0:
# combine boxes
# convert to a contour
con = [];
overlaps.append(index);
for ind in overlaps:
tl, br = boxes[ind];
con.append([tl]);
con.append([br]);
con = np.array(con);
# get bounding rect
x,y,w,h = cv2.boundingRect(con);
# stop growing
w -= 1;
h -= 1;
merged = [[x,y], [x+w, y+h]];
# highlights
highlight = merged[:];
points = con;
# remove boxes from list
overlaps.sort(reverse = True);
for ind in overlaps:
del boxes[ind];
boxes.append(merged);
# set flag
finished = False;
break;
# increment
index -= 1;
cv2.destroyAllWindows();
# show final
copy = np.copy(orig);
for box in boxes:
cv2.rectangle(copy, tup(box[0]), tup(box[1]), (0,200,0), 1);
cv2.imshow("Final", copy);
cv2.waitKey(0);
I made a ball tracking program using this guide: https://www.pyimagesearch.com/2015/09/14/ball-tracking-with-opencv/
I wanted to ask if there is a way I can tell how many bounces a ball makes in a certain time. Or even any method I can use to count the bounces of the ball on the ground, because I intend to use the program to track someone doing basketball dribbling training. Thank you in advance :)
I want to make something similar to this: https://youtu.be/OMXYvkryF1I at 2:26
Here is my code if it helps:
# import the necessary packages
from collections import deque
#list like data structure will keep prev positions of ball
#can make a trail of the ball from it
import numpy as np
import argparse
import imutils
#this is that guys list of Opencv stuff he uses - got resizing and all - can use pip to get it
#$ pip install --upgrade imutils
import cv2
import time
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
help="C:/Object_detection/models-master/research/object_detection/test_images/multi_angle.mp4")
#can put path to video here. That is if it is there
#if not there the program will just use the webcam
ap.add_argument("-b", "--buffer", type=int, default=64,
help="max buffer size")
# this tells max size of deque which is the list with points
args = vars(ap.parse_args())
##Put lower & upper boundaries of colour
#colourLow = (0, 135, 30)
#colourHigh = (19, 255, 255)
#Put lower & upper boundaries of colour
colourLow = (0, 135, 30)
colourHigh = (19, 255, 255)
pts = deque(maxlen=args["buffer"]) #initialises our deque points
# if a video path was not supplied, grab the reference
# to the webcam
# item that tells if we using a video or webcam
if not args.get("video", False):
cap = cv2.VideoCapture(0) #imutils.Video stream item works good with webcam
# otherwise, grab a reference to the video file
else:
cap = cv2.VideoCapture(args["video"]) #this is if the video is supplied
#Loop for video frame capturing
while True:
#calls the read method in our capture module
ret, frame = cap.read()
#if we were running a video from external source and no other frame was taken again for processing
#it means we reached end of video so we break out of loop
if frame is None:
break
frame = imutils.resize(frame, width=800) #smaller frames means faster processing so we resize
blurred = cv2.GaussianBlur(frame, (11, 11), 0) #blur reduces picture noise to allow us to see stuff more clearly
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # converting frame to HSV
# we now masking to get the desired colour only
# we do erosion, dilation and removal of blobs
mask = cv2.inRange(hsv, colourLow, colourHigh) #locates our object in the frame
mask = cv2.erode(mask, None, iterations=2) #erosion
mask = cv2.dilate(mask, None, iterations=2) #removal of blobs
# Will draw outline of ball and find (x, y) center of ball
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2] #this makes sure contour will work on all opencv items
center = None #make the coords of the ball 0 at first
if len(cnts) > 0: # only proceed if at least one contour was found
# finds largest contour mask, then uses this to get minimum enclosing circle and center
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) #this & above line get centre coords
# only proceed if the radius meets a minimum size
if (radius > 30):
# draw the circle and centroid on the frame,
# then update the list of tracked points
cv2.circle(frame, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
# update list of points
pts.appendleft(center)
# loop over set of points
for i in range(1, len(pts)):
#if we don't have tracked points we should ignore them
if pts[i - 1] is None or pts[i] is None:
continue
ickk = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
def drawline(img,pt1,pt2,color,thickness=ickk,style='dotted',gap=20):
dist =((pt1[0]-pt2[0])**2+(pt1[1]-pt2[1])**2)**.5
pts= []
for i in np.arange(0,dist,gap):
r=i/dist
x=int((pt1[0]*(1-r)+pt2[0]*r)+.5)
y=int((pt1[1]*(1-r)+pt2[1]*r)+.5)
p = (x,y)
pts.append(p)
if style=='dotted':
for p in pts:
cv2.circle(img,p,thickness,color,-1)
else:
s=pts[0]
e=pts[0]
i=0
for p in pts:
s=e
e=p
if i%2==1:
cv2.line(img,s,e,color,thickness)
i+=1
#if we do we will draw point connecting line
#gotta define the thickness first
thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
#cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
drawline(frame,pts[i - 1], pts[i],(0, 0, 255),thickness)
# show the frame to our screen
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# if the 'q' key is pressed, stop the loop
if key == ord("q"):
break
# cleanup the camera and close any open windows
cap.release()
cv2.destroyAllWindows()
I set up a simulation to show what I was talking about in the comments. Basically, every time the camera takes a picture (whatever fps your camera runs at) you can get the ball's position. Using that position you can estimate velocity (change in position divided by time). If there's a sudden change in the direction of that velocity then you can count that as a bounce.
The vast majority of this code is for setting up the simulation and can be safely ignored for your purposes. Here's the relevant code block
# check if it's time for a snapshot
camera_timer += dt; # time since last snapshot
if camera_timer > (1.0 / camera_fps):
# estimate velocity
est_vel[0] = (ball_pos[0] - prev_pos[0]) / camera_timer;
est_vel[1] = (ball_pos[1] - prev_pos[1]) / camera_timer;
# check if the sign of the velocity has changed
if sign(est_vel[0]) != sign(prev_est_vel[0]) or sign(est_vel[1]) != sign(prev_est_vel[1]):
# check for bounces from large change in velocity
dvx = abs(est_vel[0] - prev_est_vel[0]);
dvy = abs(est_vel[1] - prev_est_vel[1]);
change_vel = math.sqrt(dvx*dvx + dvy*dvy);
if change_vel > bounce_thresh:
bounce_count += 1;
# update previous state trackers
prev_est_vel = est_vel[:];
prev_pos = ball_pos[:];
# reset camera timer
camera_timer = 0;
snap = True;
And here's the entire thing if you want to play with the simulation yourself
import cv2
import numpy as np
import time
import math
# get mouse click
click_pos = None;
click = False;
def mouseClick(event, x, y, flags, param):
# hook to globals
global click_pos;
global click;
# check for left mouseclick
if event == cv2.EVENT_LBUTTONDOWN:
click = True;
click_pos = (x,y);
# return sign of number
def sign(val):
if val > 0:
return 1;
if val < 0:
return -1;
return 0;
# create blank image
res = (600,600,3);
bg = np.zeros(res, np.uint8);
display = np.zeros(res, np.uint8);
# set up click callback
cv2.namedWindow("Display");
cv2.setMouseCallback("Display", mouseClick);
click_force = 1000;
# font stuff
font = cv2.FONT_HERSHEY_SIMPLEX;
fontScale = 1;
fontColor = (255, 100, 0);
thickness = 2;
# make a ball
ball_radius = 20;
ball_pos = [300,300];
ball_vel = [0,0];
# set physics
drag = 0.98;
bounce_mult = 0.95;
grav = -9.8; # acceleration in pixels per second
time_scale = 5.0;
# register click animations
click_anims = [];
anim_dur = 0.25; # seconds
anim_radius = 20; # pixels
# track bounces
prev_pos = ball_pos[:];
est_vel = [0,0];
prev_est_vel = [0,0];
bounce_count = 0;
bounce_thresh = 10; # velocity must have a sudden change greater than this magnitude to count
camera_fps = 24; # we'll only take snapshots at this speed
camera_timer = 0; # time since last snapshot
snap = False;
pic_count = 0;
# loop
done = False;
prev_time = time.time();
while not done:
# refresh display
display = np.copy(bg);
# update timestep
now_time = time.time();
dt = now_time - prev_time;
dt *= time_scale;
prev_time = now_time;
# update physics
# position
ball_pos[0] += ball_vel[0] * dt;
ball_pos[1] += ball_vel[1] * dt;
# velocity
ball_vel[1] -= grav * dt;
drag_mult = (1 - ((1 - drag) * dt));
ball_vel[0] *= drag_mult;
ball_vel[1] *= drag_mult;
# check for mouse click
if click:
# register animation
click = False;
click_anims.append([time.time(), click_pos[:]]);
# get dist
dx = ball_pos[0] - click_pos[0];
dy = ball_pos[1] - click_pos[1];
dist = math.sqrt(dx*dx + dy*dy);
# clamp dist
if dist < 1:
dist = 1;
# get force attenuation
# force = click_force / (dist*dist); # too much
force = click_force / dist;
# get angle and get axial force
angle = math.atan2(dy, dx);
xforce = math.cos(angle) * force;
yforce = math.sin(angle) * force;
# apply force
ball_vel[0] += xforce;
ball_vel[1] += yforce;
# check for bounce
# left
if ball_pos[0] - ball_radius < 0:
ball_pos[0] = 0 + ball_radius;
ball_vel[0] *= -bounce_mult;
# right
if ball_pos[0] + ball_radius > res[0]:
ball_pos[0] = res[0] - ball_radius;
ball_vel[0] *= -bounce_mult;
# up # +y-axis is down in OpenCV
if ball_pos[1] - ball_radius < 0:
ball_pos[1] = 0 + ball_radius;
ball_vel[1] *= -bounce_mult;
# down
if ball_pos[1] + ball_radius > res[1]:
ball_pos[1] = res[1] - ball_radius;
ball_vel[1] *= -bounce_mult;
# check if it's time for a snapshot
camera_timer += dt; # time since last snapshot
if camera_timer > (1.0 / camera_fps):
# estimate velocity
est_vel[0] = (ball_pos[0] - prev_pos[0]) / camera_timer;
est_vel[1] = (ball_pos[1] - prev_pos[1]) / camera_timer;
# check if the sign of the velocity has changed
if sign(est_vel[0]) != sign(prev_est_vel[0]) or sign(est_vel[1]) != sign(prev_est_vel[1]):
# check for bounces from large change in velocity
dvx = abs(est_vel[0] - prev_est_vel[0]);
dvy = abs(est_vel[1] - prev_est_vel[1]);
change_vel = math.sqrt(dvx*dvx + dvy*dvy);
if change_vel > bounce_thresh:
bounce_count += 1;
# update previous state trackers
prev_est_vel = est_vel[:];
prev_pos = ball_pos[:];
# reset camera timer
camera_timer = 0;
snap = True;
# draw bounce text
cv2.putText(display, "Bounces: " + str(bounce_count), (15,40), font,
fontScale, fontColor, thickness, cv2.LINE_AA);
# draw ball
x, y = ball_pos;
cv2.circle(display, (int(x), int(y)), ball_radius, (220,150,0), -1);
# draw click animations
for a in range(len(click_anims)-1, -1, -1):
# get lifetime
life = now_time - click_anims[a][0];
if life > anim_dur:
del click_anims[a];
else:
# draw
mult = life / anim_dur;
radius = int(anim_radius * mult);
if radius > 0:
val = 255 - int(255 * mult);
color = [val, val, val];
cv2.circle(display, click_anims[a][1], radius, color, 2);
# show
cv2.imshow("Display", display);
key = cv2.waitKey(1);
# # if snapshot, save a picture
# if snap:
# snap = False;
# cv2.imwrite("bouncy/" + str(pic_count).zfill(5) + ".png", display);
# pic_count += 1;
# check keypresses
done = key == ord('q');
I am using python for watermark image source code from this
import Image
import ImageEnhance
import random
def _percent(var):
"""
Just a simple interface to the _val function with a more meaningful name.
"""
return _val(var, True)
def _int(var):
"""
Just a simple interface to the _val function with a more meaningful name.
"""
return _val(var)
def _val(var, is_percent=False):
"""
Tries to determine the appropriate value of a particular variable that is
passed in. If the value is supposed to be a percentage, a whole integer
will be sought after and then turned into a floating point number between
0 and 1. If the value is supposed to be an integer, the variable is cast
into an integer.
"""
try:
if is_percent:
var = float(int(var.strip('%')) / 100.0)
else:
var = int(var)
except ValueError:
raise ValueError('invalid watermark parameter: ' + var)
return var
def reduce_opacity(img, opacity):
"""
Returns an image with reduced opacity.
"""
assert opacity >= 0 and opacity <= 1
if img.mode != 'RGBA':
img = img.convert('RGBA')
else:
img = img.copy()
alpha = img.split()[3]
alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
img.putalpha(alpha)
return img
def determine_scale(scale, img, mark):
"""
Scales an image using a specified ratio or 'F'. If `scale` is 'F', the
image is scaled to be as big as possible to fit in `img` without falling off
the edges. Returns the scaled `mark`.
"""
if scale:
try:
scale = float(scale)
except (ValueError, TypeError):
pass
if type(scale) in (str, unicode) and scale.lower() == 'f':
# scale, but preserve the aspect ratio
scale = min(
float(img.size[0]) / mark.size[0],
float(img.size[1]) / mark.size[1]
)
elif type(scale) not in (float, int):
raise ValueError(
'Invalid scale value "%s"! Valid values are 1) "F" for ratio-preserving scaling and 2) floating-point numbers and integers greater than 0.' % (
scale,))
# determine the new width and height
w = int(mark.size[0] * float(scale)) / 2
h = int(mark.size[1] * float(scale)) / 2
print w, h
# apply the new width and height, and return the new `mark`
return (w, h)
else:
print 'Mark Size', mark.size
return mark.size
def determine_rotation(rotation, mark):
"""
Determines the number of degrees to rotate the watermark image.
"""
if (isinstance(rotation, str) or isinstance(rotation, unicode)) \
and rotation.lower() == 'r':
rotation = random.randint(0, 359)
else:
rotation = _int(rotation)
return rotation
def determine_position(position, img, mark):
"""
Options:
TL: top-left
TR: top-right
BR: bottom-right
BL: bottom-left
C: centered
R: random
X%xY%: relative positioning on both the X and Y axes
X%xY: relative positioning on the X axis and absolute positioning on the
Y axis
XxY%: absolute positioning on the X axis and relative positioning on the
Y axis
XxY: absolute positioning on both the X and Y axes
"""
max_left = max(img.size[0] - mark.size[0], 0)
max_top = max(img.size[1] - mark.size[1], 0)
if not position:
position = 'r'
if isinstance(position, tuple):
left, top = position
elif isinstance(position, str) or isinstance(position, unicode):
position = position.lower()
# corner positioning
if position in ['tl', 'tr', 'br', 'bl']:
if 't' in position:
top = 0
elif 'b' in position:
top = max_top
if 'l' in position:
left = 0
elif 'r' in position:
left = max_left
# center positioning
elif position == 'c':
left = int(max_left / 2)
top = int(max_top / 2)
# random positioning
elif position == 'r':
left = random.randint(0, max_left)
top = random.randint(0, max_top)
# relative or absolute positioning
elif 'x' in position:
left, top = position.split('x')
if '%' in left:
left = max_left * _percent(left)
else:
left = _int(left)
if '%' in top:
top = max_top * _percent(top)
else:
top = _int(top)
print 'top left', left, top
return (left, top)
def watermark(img, mark, position=(0, 0), opacity=1, scale=1.0, tile=False,
greyscale=False, rotation=0, return_name=False, **kwargs):
"""
Adds a watermark to an image.
"""
if opacity < 1:
mark = reduce_opacity(mark, opacity)
if type(scale) != tuple:
scale = determine_scale(scale, img, mark)
print 'mark mode', mark.mode
mark = mark.resize(scale)
if greyscale and mark.mode != 'LA':
mark = mark.convert('LA')
rotation = determine_rotation(rotation, mark)
if rotation != 0:
# give some leeway for rotation overlapping
new_w = mark.size[0]
new_h = mark.size[1]
new_mark = Image.new('RGBA', (new_w, new_h), (0, 0, 0, 0))
# new_mark.putalpha()
# center the watermark in the newly resized image
new_l = (new_w - mark.size[0]) / 2
new_t = (new_h - mark.size[1]) / 2
new_mark.paste(mark, (new_l, new_t))
mark = new_mark.rotate(rotation, Image.BICUBIC, expand=True)
position = determine_position(position, img, mark)
print 'image mode', img.mode
print 'mark mode', mark.mode
if img.mode != 'RGBA':
img = img.convert('RGBA')
print 'image ---', img.mode
alpha = img.split()[3]
alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
img.putalpha(alpha)
# make sure we have a tuple for a position now
assert isinstance(position, tuple), 'Invalid position "%s"!' % position
# create a transparent layer the size of the image and draw the
# watermark in that layer.
layer = Image.new('RGBA', img.size, (0, 0, 0, 0))
if tile:
first_y = position[1] % mark.size[1] - mark.size[1]
first_x = position[0] % mark.size[0] - mark.size[0]
for y in range(first_y, img.size[1], mark.size[1]):
for x in range(first_x, img.size[0], mark.size[0]):
layer.paste(mark, (x, y))
else:
layer.paste(mark, position)
# composite the watermark with the layer
return Image.composite(layer, img, layer)
def test():
im = Image.open('/home/chanhle/0450034_a (4th copy).jpg')
mark = Image.open('/home/chanhle/UMA LOGO.png')
# im.save('/home/chanhle/0120047_a.png')
watermark(im, mark,
position='C',
opacity=0.8,
scale='f',
rotation=45).save('/home/chanhle/test3.jpg', 'JPEG')
if __name__ == '__main__':
test()
This is Image I want to watermark
here is logo
and result when run code above
Result when I use online tools it's beautiful.
As you see in result logo watermark is not sharp enough it's not beautiful as I expected.
How to improve this quality?
Thank for support.
Change:
mark = mark.resize(scale)
to:
mark = mark.resize(scale, Image.ANTIALIAS)
change the opacity to 0.5. And try saving as a PNG image or add quality=100 to the save arguments if using JPEG.