Why is OpenCV creating weird rectangles? - python

I am making a software to track the changes in the areas of different bubbles over the span of a video. I am trying to use the OpenCV multitracker to track these circles, which were created by a Hough's Circle Transformation. My understanding is, I need to define a bounding box to pass to this multitracker tool. So I made the box with basic geometry, but it gave a weird result when I was displaying the box, through the code seen below:
import cv2
import numpy as np
import matplotlib.pyplot as plt
vidObj = cv2.VideoCapture('Data/video.mp4')
success = True
tracked = False
multiTracker = cv2.MultiTracker_create()
def Find_Circles(img):
# Convert to gray-scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Blur the image to reduce noise
img_blur = cv2.bilateralFilter(gray, 7, 50, 50)
# Apply hough transform on the image
circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, 70, param1=110, param2=10, minRadius=20, maxRadius=100)
return circles
def Draw_Circles(img,circles,tracked):
if circles is not None:
circles = np.uint16(np.around(circles))
if tracked is False:
for i in circles[0, :]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0),2)
cv2.rectangle(img, (i[0]-i[2], i[1]-i[2]), (i[0]+i[2], i[1]+i[2]), (0, 255, 0),2)
else:
(success, circles) = multiTracker.update(img)
print(circles)
while success:
success, img = vidObj.read()
if img is not None:
Draw_Circles(img, Find_Circles(img),tracked)
tracked = True
imgplot = plt.imshow(img,cmap='gray')
plt.show()
else:
success = False
Here is the result. Any ideas why? Can provide video in mp4 format if needed.

This line causes error:
circles = np.uint16(np.around(circles))
Values in "circles" can be negative, so you can not use uint16 (unsigned integer) type. Use int16:
circles = np.int16(np.around(circles))

Related

Opencv HoughCircles missing obvious circle

I have tried some variations in the parameters, read a detailed description of their meaning, but I can't seem to detect a simple circle in an image. This is a simplified function I have tried:
def get_a_circles(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, # input grayscale image
cv2.HOUGH_GRADIENT,
2,
minDist=5,
param1=10, param2=200,
minRadius=0,
maxRadius=0)
return circles
which, when run on this image:
img = cv2.imread("step2.jpg")
get_a_circle(img.copy())
returns none.
It does however detect the circles in this image:
image
circles found & highlighted
I tried to add some blur to the image that fails, with either
gray = cv2.medianBlur(gray, 5) or gray = cv2.GaussianBlur(gray,(5,5),0) but it does not improve the results.
Any suggestions on what to try with that image? (It would seem it's an obvious circle)
You need to lower param2 in HoughCircles in Python/OpenCV.
Input:
import cv2
import numpy as np
# Read image
img = cv2.imread('dot.jpg')
hh, ww = img.shape[:2]
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# median filter
gray = cv2.medianBlur(gray, 5)
# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=150, param2=10, 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, 0, 255), 1)
# save results
cv2.imwrite('dot_circle.jpg', result)
# show images
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

HoughCircles circle detection not working?

I have been trying to write a program that can detect circles on my screen.
This is my screen before code processing
As you can see on the image, there are three circles that the code should detect. I am using HoughCircles function from OpenCV library to achieve this task. My code is below.
ss = gui.screenshot()
img = cv2.cvtColor(np.array(ss), cv2.COLOR_RGB2BGR)
output = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
if circles is not None:
print("circles found", len(circles))
circles = np.round(circles[0, :]).astype("int")
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)
cv2.imshow("output", np.hstack([gray, output]))
cv2.waitKey(0)
cv2.imshow("output", gray)
cv2.waitKey(0)
I am first taking screenshot of my screen. Then, I convert it to use it for opencv.
However, this code does not detect any circles for the screenshot shown in the first picture. I know this because when ran, my program does not print "circles found". Moreover, to show that I have been taking screenshots and transforming them to grayscale properly, I have this image taken from the last two lines of my code.
picture in a gray scale
To show that my code works with other circle images, here is a picture of a regular circle:
before detection
after detection
Any help would be very appreciated!
Here's an alternative solution to detect the circles without using the Hough Transform. As your input image has a very distinct blue hue to the blobs of interest, you can try to create a segmentation mask based on their HSV values. Then, detect contours and approximate each contour using a circle. The last step can be implemented using the cv2.minEnclosingCircle, which, as its name suggest, can compute the Minimum Enclosing Circle of a contour.
Let's see the code:
# image path
path = "D://opencvImages//"
fileName = "XUzFw.png"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Create a deep copy of the input for results:
inputImageCopy = inputImage.copy()
# Convert the image to the HSV color space:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
# Set the HSV values:
lowRange = np.array([78, 0, 158])
uppRange = np.array([125, 255, 255])
# Create the HSV mask
mask = cv2.inRange(hsvImage, lowRange, uppRange)
This generates the following segmentation mask:
As you can see, the only blobs that remain are the circles. Now, let's compute the contours and find the minimum enclosing circle:
# Find the circle blobs on the binary mask:
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Use a list to store the center and radius of the target circles:
detectedCircles = []
# Look for the outer contours:
for i, c in enumerate(contours):
# Approximate the contour to a circle:
(x, y), radius = cv2.minEnclosingCircle(c)
# Compute the center and radius:
center = (int(x), int(y))
radius = int(radius)
# Draw the circles:
cv2.circle(inputImageCopy, center, radius, (0, 0, 255), 2)
# Store the center and radius:
detectedCircles.append([center, radius])
# Let's see the results:
cv2.namedWindow("Circles", cv2.WINDOW_NORMAL)
cv2.imshow("Circles", inputImageCopy)
cv2.waitKey(0)
This is the result of the detection:
Additionally, you can check out the data stored in the detectedCircles list:
# Check out the detected circles:
for i in range(len(detectedCircles)):
# Get circle data:
center, r = detectedCircles[i]
# Print it:
print("i: "+str(i)+" x: "+str(center[0])+" y: "+str(center[1])+" r: "+str(r))
Which yields:
i: 0 x: 395 y: 391 r: 35
i: 1 x: 221 y: 391 r: 36
i: 2 x: 567 y: 304 r: 35
These are the parameters of houghCircles that works for me. You should also consider running a gaussian blur over the image before trying to find the circles.
I'm not a huge fan of houghCircles. I find it to be really finicky and I don't like how much of what it does is hidden inside the function. It makes tuning it mostly trial-and-error. These parameters work for this particular image, but I wouldn't count on this continuing to work under different lighting conditions or for different colors.
import cv2
import numpy as np
# load image
img = cv2.imread("spheres.png");
# grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
gray = cv2.GaussianBlur(gray,(5,5),0);
# circles
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp = 1, minDist = 100, param1=65, param2=20, minRadius=20, maxRadius=50)
# draw circles
if circles is not None:
# round to ints
circles = np.uint16(np.around(circles));
for circle in circles[0, :]:
# unpack and draw
x, y, radius = circle;
center = (x,y);
cv2.circle(img, center, radius, (255, 0, 255), 3);
# show
cv2.imshow("Image", img);
cv2.imshow("Gray", gray);
cv2.waitKey(0);

Error when performing Hough circle transformation on OpenCV-python

The code below is intended to perform a Hough Circle transformation on a video. The code works as intended when a frame from the video is extracted and an Hough Circle transformation is performed. However, when I try to replicate the similar method to produce a video I get this error loop of ufunc does not support argument 0 of type NoneType which has no callable rint method. The cause of this error seems to be below the while loop. What could be the cause of this issue?
Many thanks in advance.
Update: I have changed my blurred = cv2.medianBlur(dframe, 25) to blurred = cv2.GaussianBlur(dframe,(11,11),0). This seem to work for a moment but then the code crashes to give me the same error.
import numpy as np
import cv2
import matplotlib.pyplot as plt
np.random.seed(42)
def fixColor(image):
return(cv2.cvtColor(image,cv2.COLOR_BGR2RGB))
video = cv2.VideoCapture("video.wmv")
#randomly select frames in an array
frameIds = video.get(cv2.CAP_PROP_FRAME_COUNT) *np.random.uniform(size = 55)
#store selected frames in an array
frames = []
for fid in frameIds:
video.set(cv2.CAP_PROP_FRAME_COUNT,fid)
ret, frame = video.read()
frames.append(frame)
video.release()
#calculate the median frame
medianFrame = np.median(frames,axis=0).astype(dtype=np.uint8)
#random sample
sample_frame = frames[0]
#convert to grayscale
grayMedianFrame = cv2.cvtColor(medianFrame, cv2.COLOR_BGR2GRAY)
graySample = cv2.cvtColor(sample_frame, cv2.COLOR_BGR2GRAY)
#subtract grayscale frames between sample and median
dframe = cv2.absdiff(graySample, grayMedianFrame)
blurred = cv2.medianBlur(dframe, 25)
#plt.imshow(fixColor(blurred))
#Hough circle
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT,1,120,param1= 50, param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# draw outer circle
cv2.circle(sample_frame, (i[0], i[1]), i[2], (0, 255, 0), 2)
# draw center of circle
cv2.circle(sample_frame, (i[0], i[1]), 2, (0, 255, 0), 9)
plt.imshow(sample_frame,cmap="gray") #outputs image from sample frame with Hough circle
#write in new video
writer = cv2.VideoWriter("output_hough.mp4",cv2.VideoWriter_fourcc(*"MP4V"),30,(512,512))
video = cv2.VideoCapture("video.wmv")
total_frames = video.get(cv2.CAP_PROP_FRAME_COUNT)
frameCnt = 0
########## CODE WORKS FINE UNTIL THIS POINT ##############
while(frameCnt < total_frames-1):
frameCnt +=1
ret, frame = video.read() #read frame 1 by 1
gframe = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #grayscale frame
dframe = cv2.absdiff(gframe, grayMedianFrame) #remove background
blurred = cv2.medianBlur(dframe, 25) #blur
#Hough transformation
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT,1,120,param1= 50, param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# draw outer circle
cv2.circle(frame, (i[0], i[1]), i[2], (0, 255, 0), 2)
# draw center of circle
cv2.circle(frame, (i[0], i[1]), 2, (0, 255, 0), 9)
writer.write(cv2.resize(frame,(512,512))) #write frame into output vid
video.release()
writer.release()
You did not show the full stack and the exact line that is throwing the error.
But, looking at your code I guess the problem lies in:
circles = np.uint16(np.around(circles))
What happens is that cv2.HoughCircles can return None if it can not find circles, then np.around is not working with None.
What you should do is check first that you got your circles by if circles is not None:

How to remove an extension to a blob caused by morphology

I have an image that I'm eroding and dilating like so:
kernel = np.ones((5,5),np.float32)/1
eroded_img = cv2.erode(self.inpainted_adjusted_image, kernel, iterations=10)
dilated_img = cv2.dilate(eroded_img, kernel, iterations=10)
Here's the result of the erosion and dilation:
and then I'm taking a threshold of it like so:
self.thresh = cv2.threshold(dilated_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
But the threshold gives me an unwanted extension that I've marked in the image below (The region above the red line is the unwanted region):
How do I get rid of this unwanted region? Is there a better way to do what I'm doing?
Working with a different type of threshold (adaptive threshold, which takes local brigthness into account) will already get rid of your problem: The adaptive threshold result is what you are looking for.
[EDIT: I have taken the liberty of adding some code on Hough circles. I admit that I have played with the parameters for this single image to get a nice looking result, though I do not know what type of accuracy you are needing for such a type of problem]
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('image.png',0)
thresh = cv2.threshold(img, 210, 255, cv2.ADAPTIVE_THRESH_MEAN_C)[1]
canny = cv2.Canny(thresh,50,150)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(canny,cv2.HOUGH_GRADIENT,1,20, param1=50,param2=23,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],(255,0,0),3)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
titles = ['Original Image', 'Adaptive Thresholding', "Canny", "Hough Circle"]
images = [img, thresh, canny, cimg]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
Let us know if this is not yet sufficient.
From the binary Image it would be fairly easy to fit a circle using a Hough transform. Once you have the outer boundary of the circle i would suggest bleeding the boundary and cropping out the portion that outside the boundary.
Another approach is to adjust your threshold value. It looks like you could get away with that. You might need some morphological operations to get a clean edge. Using a disk kernel will help retain the shape to a good extent.
Since your question has been rolled back to its original version, I have attached a solution using flood fill which works on your images.
import numpy as np
import cv2
import sys
import matplotlib.pyplot as plt
img = cv2.imread('image.png', 0)
h, w = img.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
gray = cv2.blur(img,(5,5))
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
print maxLoc
fixed_range = True
connectivity = 4
flooded = img.copy()
mask[:] = 0
connectivity = 4 #8
flags = connectivity
flags |= cv2.FLOODFILL_FIXED_RANGE
cv2.floodFill(flooded, mask, maxLoc, (255, 255, 255), (60,)*3, (60,)*3, flags)
thresh = cv2.threshold(flooded, 250, 255, cv2.THRESH_BINARY)[1]
titles = ['Original Image', 'Blurred', "Floodfill", "Threshold"]
images = [img, gray, flooded, thresh]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()

Find all circles in image

I am new to python & image processing. I am working on a hobby project in which I want to find ALL circles in image and then figure out which one have cross ('X') marked inside it. I have put some code together to find circles so far (below). It works on one image but fails to recognize all circles on another one. Please guide me how i can improve performance of find_circles algorithm.
Test Image:
Result Image:
import cv2
import cv
import numpy as np
import operator
from PIL import Image
def find_circles(img):
im_gray = cv2.imread(img, cv2.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, im_bw) = cv2.threshold(im_gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
img_bw = cv2.threshold(im_gray, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('img_bw.png',img_bw)
rows, cols =img_bw.shape
circles = cv2.HoughCircles(img_bw,cv.CV_HOUGH_GRADIENT,1,rows/32, param1=100,param2=40,minRadius=0,maxRadius=100)
circles = np.uint16(np.around(circles))
return circles
def draw_circles(img, circles):
img = cv2.imread(img,0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
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,0,255),3)
cv2.putText(cimg,str(i[0])+str(',')+str(i[1]), (i[0],i[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.4, 255)
return cimg
def main():
img = "query_circle9.png"
circles = find_circles(img)
img_circle = draw_circles(img,circles)
cv2.imwrite('cricle.png',img_circle)
if __name__=='__main__':
main()
#!/usr/bin/env python
import cv2
def draw_circles(img, circles):
# img = cv2.imread(img,0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
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,0,255),3)
cv2.putText(cimg,str(i[0])+str(',')+str(i[1]), (i[0],i[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.4, 255)
return cimg
def detect_circles(image_path):
gray = cv2.imread(image_path, cv2.CV_LOAD_IMAGE_GRAYSCALE)
gray_blur = cv2.medianBlur(gray, 13) # Remove noise before laplacian
gray_lap = cv2.Laplacian(gray_blur, cv2.CV_8UC1, ksize=5)
dilate_lap = cv2.dilate(gray_lap, (3, 3)) # Fill in gaps from blurring. This helps to detect circles with broken edges.
# Furture remove noise introduced by laplacian. This removes false pos in space between the two groups of circles.
lap_blur = cv2.bilateralFilter(dilate_lap, 5, 9, 9)
# Fix the resolution to 16. This helps it find more circles. Also, set distance between circles to 55 by measuring dist in image.
# Minimum radius and max radius are also set by examining the image.
circles = cv2.HoughCircles(lap_blur, cv2.cv.CV_HOUGH_GRADIENT, 16, 55, param2=450, minRadius=20, maxRadius=40)
cimg = draw_circles(gray, circles)
print("{} circles detected.".format(circles[0].shape[0]))
# There are some false positives left in the regions containing the numbers.
# They can be filtered out based on their y-coordinates if your images are aligned to a canonical axis.
# I'll leave that to you.
return cimg
The result:
cimg = detect_circles("circles.png")
There are some left-over false detection. If your images are aligned, then you can filter these false positives based on their y-coordinates. I'll leave that to you.

Categories