I'm relatively new to scripting. (I know quite a bit but I also don't know quite a bit.)
I'm trying to have a simple script use OpenCV-Python to subtract two frames from a webcam and draw a bounding box around the changed pixels. The issue is that when I try to define the boundingRect (x,y,w,h = cv2.boundingRect(contours)) it gives the error:
Message=OpenCV(4.5.3) :-1: error: (-5:Bad argument) in function 'boundingRect'
> Overload resolution failed:
> - array is not a numpy array, neither a scalar
> - Expected Ptr<cv::UMat> for argument 'array'
I've been searching around for quite a while but there seems to be a very small number of people who've had my issue and pretty much none of them had solutions that worked.
Here's my code:
import cv2
from time import sleep as wait
import numpy as np
lastFrame = "foobaz"
i = 0
#My webcam is on index 1, this isn't (at least shouldn't) be the issue. Make sure to set it back to 0 if you are testing
vid = cv2.VideoCapture(1)
#A placeholder black image for the 'subract' imshow window
black = np.zeros((512,512,3), np.uint8)
while(True):
wait(0.5)
ret, frame = vid.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blurframe = cv2.GaussianBlur(frame,(25,25),0)
#Makes sure the lastFrame has been assigned, if not set it as placeholder black image.
if lastFrame != "foobaz":
#Subtracts current frame and the last frame to find difference.
subFrame = cv2.subtract(blurframe,lastFrame)
else:
subFrame = black
#Assigns the next lastFrame
lastFrame = blurframe
#Gets the threshold of the subtracted image
ret,thresh1 = cv2.threshold(subFrame,40,255,cv2.THRESH_BINARY)
#Sets the thresholded image to grayscale if the loop was ran for the first time.
if i==0:
thresh1 = cv2.cvtColor(thresh1, cv2.COLOR_BGR2GRAY)
i+=1
#This is where issues arize. I'm trying to apply a bounding box using a contour but it always errors at line 44.
contours = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
print(len(contours))
x,y,w,h = cv2.boundingRect(contours)
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('frame',frame)
cv2.imshow('subtract',thresh1)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
I saw that some other posts had the contour type() so here it is:
type(contours) = <class 'list'>
CLOSED: I found out the issue. You have to iterate contours for it to work.
Related
I am very new to OpenCV Python and I really need some help here.
So what I am trying to do here is to extract out these words in the image below.
The words and shapes are all hand drawn, so they are not perfect. I have did some coding below.
First of all, I grayscale the image
img_final = cv2.imread(file_name)
img2gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
Then I use THRESH_INV to show the content
ret, new_img = cv2.threshold(image_final, 100 , 255, cv2.THRESH_BINARY_INV)
After which, I dilate the content
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3 , 3))
dilated = cv2.dilate(new_img,kernel,iterations = 3)
I dilate the image is because I can identify text as one cluster
After that, I apply boundingRect around the contour and draw around the rectangle
contours, hierarchy = cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) # get contours
index = 0
for contour in contours:
# get rectangle bounding contour
[x,y,w,h] = cv2.boundingRect(contour)
#Don't plot small false positives that aren't text
if w < 10 or h < 10:
continue
# draw rectangle around contour on original image
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,255),2)
This is what I got after that.
I am only able to detect one of the text. I have tried many other methods but this is the closet results I have got and it does not fulfill the requirement.
The reason for me to identify the text is so that I can get the X and Y coordinate of each of the text in this image by putting a bounding Rectangle "boundingRect()".
Please help me out. Thank you so much
You can use the fact that the connected component of the letters are much smaller than the large strokes of the rest of the diagram.
I used opencv3 connected components in the code but you can do the same things using findContours.
The code:
import cv2
import numpy as np
# Params
maxArea = 150
minArea = 10
# Read image
I = cv2.imread('i.jpg')
# Convert to gray
Igray = cv2.cvtColor(I,cv2.COLOR_RGB2GRAY)
# Threshold
ret, Ithresh = cv2.threshold(Igray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Keep only small components but not to small
comp = cv2.connectedComponentsWithStats(Ithresh)
labels = comp[1]
labelStats = comp[2]
labelAreas = labelStats[:,4]
for compLabel in range(1,comp[0],1):
if labelAreas[compLabel] > maxArea or labelAreas[compLabel] < minArea:
labels[labels==compLabel] = 0
labels[labels>0] = 1
# Do dilation
se = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(25,25))
IdilateText = cv2.morphologyEx(labels.astype(np.uint8),cv2.MORPH_DILATE,se)
# Find connected component again
comp = cv2.connectedComponentsWithStats(IdilateText)
# Draw a rectangle around the text
labels = comp[1]
labelStats = comp[2]
#labelAreas = labelStats[:,4]
for compLabel in range(1,comp[0],1):
cv2.rectangle(I,(labelStats[compLabel,0],labelStats[compLabel,1]),(labelStats[compLabel,0]+labelStats[compLabel,2],labelStats[compLabel,1]+labelStats[compLabel,3]),(0,0,255),2)
I am trying to find 2 circles in a field and mark them from the live data I am getting from the camera. The problem is that the houghCircles() function cannot always detect those circles even if their position is unchanged. Here is the code:
import cv2
import numpy as np
import time
wait = 5
st = time.clock()
on=0
arka=0
def circleS():
circles = cv2.HoughCircles(img_grs, cv2.HOUGH_GRADIENT, 1, 20,
param1=150,
param2=35,
minRadius=5,
maxRadius=30)
on=0
arka=0
if(circles[0,0,2]>circles[0,1,2]):
on=(circles[0,0,0],circles[0,0,1])
arka=(circles[0,1,0],circles[0,1,1])
if(circles[0,0,2]<circles[0,1,2]):
on=(circles[0,1,0],circles[0,1,1])
arka=(circles[0,0,0],circles[0,0,1])
cv2.circle(img_bgr,on,2,(0,255,0),3)
cv2.circle(img_bgr,arka,2,(0,0,255),3)
return on,arka
def takePic():
ret, frame = camera.read()
img_bgr = np.copy(frame)
frame = None
return img_bgr
camera = cv2.VideoCapture(1)
while((time.clock()-st)<=wait):
ret, frame = camera.read() # Capture a frame
#cv2.imshow('Camera Stream',frame) # Display the captured frame in a window named Camera Stream
cv2.waitKey(1)
cv2.destroyAllWindows()
while(1):
img_bgr=takePic()
img_grs = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
ret,img_bin = cv2.threshold(img_grs,220,255,cv2.THRESH_BINARY)
corners = cv2.goodFeaturesToTrack(img_bin,9,0.03,3)
corners = np.int0(corners)
on,arka=np.int0(circleS())
for i in corners:
x,y = i.ravel()
if(x>=arka[0]+15 or x<=arka[0]-15 or y>=arka[1]+15 or y<=arka[1]-15 ):
cv2.circle(img_grs,(x,y),3,255,-1)
cv2.imshow("Camera Stream",img_bgr)
cv2.waitKey(10)
img_bgr = None
img_grs= None
time.sleep(5)
camera.release()
cv2.imshow("bin",img_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()
The error I am getting is index out of bounds because of the circles array. What's going wrong?
The index out-of-bounds error on circles is because the cv2.HoughCircles() may return an empty circles array when it does not find any circle (as you mention).
Put in an extra check after the cv2.HoughCircles() call:
if len(circles) > 0:
# your circle stuff
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while(1):
# Take each frame
_, frame = cap.read()
frame = cv2.medianBlur(frame,5)
cimg = cv2.cvtColor(frame,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(frame,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,0,255),3)
cv2.imshow('frame',cimg)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
I am trying to find circles, but my code fails OpenCV Error: Assertion failed (scn == 1 && (dcn == 3 || dcn == 4)). Any idea why? In addition, how to get coordinates of a circle?
The flag you pass to cvt Color should be cv2.COLOR_BGR2GRAY instead of cv2.COLOR_GRAY2BGR - you want convert to grayscale to have single channeled image instead of RGB 3 channeled image and that's what assertion says. (look at the docs to see what paramaters you need, an input image is clearly a 8-bit, single-channel, grayscale input image.)
Rest of the code looks ok and is an answer to second part of your question.
EDIT: (after OP having another problem with passing constant):
After changing cvtColor constant we had following code:
cimg = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(cimg,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
which actually caused another problem:
AttributeError: 'module' object has no attribute 'HOUGH_GRADIENT'
Since I'm working with OpenCV mostly with C++ and not with Python, I solved it in different way. I tried to find number equivalent of CV_HOUGH_GRADIENT constant. In turned out to be located in opencv\sources\modules\imgproc\include\opencv2\imgproc\types_c.h:
enum
{
CV_HOUGH_STANDARD =0,
CV_HOUGH_PROBABILISTIC =1,
CV_HOUGH_MULTI_SCALE =2,
CV_HOUGH_GRADIENT =3
};
That is why we can use following snippet instead of predefined constant:
circles = cv2.HoughCircles(cimg,3,1,20, param1=50,param2=30,minRadius=0,maxRadius=0)
Below is output
Here are those marked in red circle. There are lots of such line the image, I have marked only 3 circle. How to remove these lines? Is there any filter to remove this line? I think, this is due to presence of corner.
You can use Median filter to remove that thing.
Try this code and see the difference while changing the value of the trackbar (mask size).
import cv2
import numpy as np
def nothing(x):
pass
#image window
cv2.namedWindow('image')
cv2.namedWindow('image2')
# create trackbars for color change
cv2.createTrackbar('mask','image',1,10,nothing)
cv2.createTrackbar('mask2','image2',1,10,nothing)
#loading images
img = cv2.imread('VfgSN.png') # load your image with path
gray_im = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
while True:
# get current positions of trackbars
m = cv2.getTrackbarPos('mask','image')
m2 = cv2.getTrackbarPos('mask2','image2')
median = cv2.medianBlur(gray_im,(2*m+1))
median2 = cv2.medianBlur(img,(2*m2+1))
cv2.imshow('image',median)
cv2.imshow('image2',median2)
#press ESC to stop
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
Resultant image with mask : 5
In grayscale (with the same mask)
But then it also affects the other parts of the image. If you don't want that to happen to the other part. You can apply this filter in your Region of Interest only.
Hope it helps.
I am trying to make a motion detect program using my webcam, but I'm getting this weird results when thresholding the difference of frames:
When Im moving: (seems okay I guess)
![enter image description here][1]
When Im not moving:
![enter image description here][2]
What can this be? I already ran a couple of programs that got exactly the same algorithm and the thresholding is doing fine..
Heres my code:
import cv2
import random
import numpy as np
# Create windows to show the captured images
cv2.namedWindow("window_a", cv2.CV_WINDOW_AUTOSIZE)
cv2.namedWindow("window_b", cv2.CV_WINDOW_AUTOSIZE)
# Structuring element
es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,4))
## Webcam Settings
capture = cv2.VideoCapture(0)
#dimensions
frameWidth = capture.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)
frameHeight = capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)
while True:
# Capture a frame
flag,frame = capture.read()
current = cv2.blur(frame, (5,5))
difference = cv2.absdiff(current, previous) #difference is taken of the current frame and the previous frame
frame2 = cv2.cvtColor(difference, cv2.cv.CV_RGB2GRAY)
retval,thresh = cv2.threshold(frame2, 10, 0xff, cv2.THRESH_OTSU)
dilated1 = cv2.dilate(thresh, es)
dilated2 = cv2.dilate(dilated1, es)
dilated3 = cv2.dilate(dilated2, es)
dilated4 = cv2.dilate(dilated3, es)
cv2.imshow('window_a', dilated4)
cv2.imshow('window_b', frame)
previous = current
key = cv2.waitKey(10) #20
if key == 27: #exit on ESC
cv2.destroyAllWindows()
break
Thanks in advance!
[1]: http://i.stack.imgur.com/hslOs.png
[2]: http://i.stack.imgur.com/7fB95.png
The first thing that you need is a previous = cv2.blur(frame, (5,5)) to prime your previous sample after a frame grab before your while loop.
This will make the code you posted work, but will not solve your problem.
I think the issue that you are having is due to the type of thresholding algorithm that you are using. Try a binary, cv2.THRESH_BINARY, instead of Otsu's. It seemed to solve the problem for me.