I have an image like below. Dimension of the image is fixed: 640x480.
I want to bound all non-zero regions with rectangles like this:
I need to know the upper-right, and lower-left points of each of these rectangles.
I have thought about loops and other methods. But all of them will take too long to run. What is the most efficient way to do this in python?
PS: I am a beginner in image processing. This might be an obvious question, i don't know. So giving me a sample code would help a lot. Thanks.
Finding all sub-components within an image is called connected component analysis. In OpenCV you can do it with findCountour() function of its contour analysis library.
Here is a sample code:
import cv2
import numpy as np
from scipy import signal
#=========================================================================
# Locate all components
#=========================================================================
def locateComponents(img):
"""Extracts all components from an image"""
out = img.copy()
res = cv2.findContours(np.uint8(out.copy()),\
cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = res[1]
ret = []
row, col = out.shape
minSiz = 8
for cnt in contours:
# get bounding box
y, x, n, m = cv2.boundingRect(cnt)
# check area
if m < minSiz or n < minSiz:
continue
#end if
ret.append(np.int32([x, x+m, y, y+n]))
out = cv2.rectangle(out, (y,x), (y+n,x+m), (255,255,255), 2)
#end for
return ret, out
# end function
#=========================================================================
# TESTING
#=========================================================================
img = cv2.imread('input.jpg', 0)
regions, out = locateComponents(img)
cv2.imwrite('output.jpg', out)
print regions
cv2.imshow('Given image', img)
cv2.imshow('Located regions', out)
cv2.waitKey(0)
The output image:
Related
I have the task of generating data for deep learning. I take seed images, rotate them and plot them randomly on a background. The issue is the rotation results in a broken line boundary around the image and I can't figure out why it appears or how to get rid of it.
def rotateSeed(img):
rotated = imutils.rotate_bound(img, randint(0,360))
for row in range(rotated.shape[0]):
for col in range(rotated.shape[1]):
if (rotated[row,col,0] == 0) and (rotated[row,col,1] == 0) and (rotated[row,col,2] == 0):
rotated[row,col] = default[0,0]
return rotated
Code explanation: default is the background color in the seed image. Rotation produces a black region I cover with the default.
Only one other person has had this problem and the solution does not explain much. It did not even rotate: OpenCV
Original seed image
Rotated seed image
You can use this code for rotation:
import cv2
import numpy as np
img = cv2.imread('C:\\Code\\1.jpeg')
num_rows, num_cols = img.shape[:2]
rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2), 30, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (num_cols, num_rows))
cv2.imshow('Rotation', img_rotation)
cv2.waitKey()
Apologies in advance as i am newbie to OpenCV-Python. I set myself a task to create a Passport type image from the video capture.
Using a head and shoulders Haar Cascade i was able to create a portrait photo but i now want to turn the background to a white background (leaving the head and shoulders portrait in the foreground).
Just not sure how/ best way to do this. Any help would be welcome.
Many thanks in advance.
Here is the code:
import numpy as np
import cv2
# face file
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
# eye file
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')
# head shoulders file
hs_cascade = cv2.CascadeClassifier('HS.xml')
cap = cv2.VideoCapture(1)
while 1:
ret, img = cap.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
headshoulders = hs_cascade.detectMultiScale(gray, 1.3, 3)
# find the head and shoulders
for (x,y,w,h) in headshoulders:
# variable change to make portrait orientation
x = int(x*1.5)
w = int(w/1.5)
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
# crop the image
crop_img = img[y: y + h, x: x + w]
# show original and crop
cv2.imshow('crop', crop_img)
cv2.imshow('img', img)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
elif k == ord('s'):
# save out the portrait image
cv2.imwrite('cropimage.png',crop_img)
# release the camera
cap.release()
cv2.destroyAllWindows()
I got it to work. Here is my solution.
PLEASE NOTE: This worked for HI-RES images (Nikon D7100 - JPEG). LOW-RES did NOT work when i tried a Webcam (Logitech C615).
I used some of the code from a link that was suggested.
# import numpy
import numpy as np
# import cv2
import cv2
# import Matplitlib
from matplotlib import pyplot as plt
# Fill any holes function
def get_holes(image, thresh):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
im_bw = cv2.threshold(gray, thresh, 255, cv2.THRESH_BINARY)[1]
im_bw_inv = cv2.bitwise_not(im_bw)
im_bw_inv, contour, _ = cv2.findContours(im_bw_inv, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contour:
cv2.drawContours(im_bw_inv, [cnt], 0, 255, -1)
nt = cv2.bitwise_not(im_bw)
im_bw_inv = cv2.bitwise_or(im_bw_inv, nt)
return im_bw_inv
# Remove background Function
def remove_background(image, thresh, scale_factor=.25, kernel_range=range(1, 15), border=None):
border = border or kernel_range[-1]
holes = get_holes(image, thresh)
small = cv2.resize(holes, None, fx=scale_factor, fy=scale_factor)
bordered = cv2.copyMakeBorder(small, border, border, border, border, cv2.BORDER_CONSTANT)
for i in kernel_range:
#kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2*i+1, 2*i+1))
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (2*i+1, 2*i+1))
bordered = cv2.morphologyEx(bordered, cv2.MORPH_CLOSE, kernel)
unbordered = bordered[border: -border, border: -border]
mask = cv2.resize(unbordered, (image.shape[1], image.shape[0]))
fg = cv2.bitwise_and(image, image, mask=mask)
return fg
# Load a color image in grayscale
img = cv2.imread('original/11.png')
# Start background removal -- Parameters are <image> and <threshold level>
nb_img = remove_background(img, 180)
# Change Black Pixels to WHITE
nb_img[np.where((nb_img==[0,0,0]).all(axis=2))] = [255,255,255]
# resize the viewing size (as the images are too big for the screen
small = cv2.resize(nb_img, (300, 400))
# Show the finished image
cv2.imshow('image',small)
k = cv2.waitKey(0) & 0xFF
if k == 27: #wait for ESC key to exit
# if ESC pressed close the camera windows
cv2.destroyAllWindows()
elif k == ord('s'): #wait for 's' key to save and exit
# Save the img(greyscale version)
cv2.imwrite('bg_removal/11.png',small)
cv2.destroyAllWindows()
I want to find the HSV value of a LASER dot using opencv and python. I got the code http://opencv-srf.blogspot.com.au/2010/09/object-detection-using-color-seperation.html from here but it is in c++, installing visual studio and opencv takes time so i changed the code in python
import cv2
import numpy as np
def callback(x):
pass
cap = cv2.VideoCapture(0)
cv2.namedWindow('image')
ilowH = 0
ihighH = 179
ilowS = 0
ihighS = 255
ilowV = 0
ihighV = 255
# create trackbars for color change
cv2.createTrackbar('lowH','image',ilowH,179,callback)
cv2.createTrackbar('highH','image',ihighH,179,callback)
cv2.createTrackbar('lowS','image',ilowS,255,callback)
cv2.createTrackbar('highS','image',ihighS,255,callback)
cv2.createTrackbar('lowV','image',ilowV,255,callback)
cv2.createTrackbar('highV','image',ihighV,255,callback)
while(1):
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
cv2.imshow('hsv', hsv)
lower_hsv = np.array([ilowH, ilowS, ilowV])
higher_hsv = np.array([ihighH, ihighS, ihighV])
mask = cv2.inRange(hsv, lower_hsv, higher_hsv)
cv2.imshow('mask', mask)
cv2.imshow('frame', frame)
print ilowH, ilowS, ilowV
if(cv2.waitKey(1) & 0xFF == ord('q')):
break
cv2.destroyAllWindows()
cap.release()
but this code doesnot threshold anything. It seems like the trackbars i created doesnot change the value of ilowH ,ilowS, ilowV . I checked it by printing those values inside while loop. What could be the problem for not thresholding any of those values or is there better code in python to find HSV values of the LASER.
Thank you, any help is appreciated.
You can grab the trackbar values with cv2.getTrackbarPos(). Also note that sometimes it puts trackbars out of order, which is annoying, but at least they're labeled.
However, I don't think that these trackbars will work very well for live video feed. There's a lot of freezing issues. You'll have to have a super low framerate (works for me with cv2.waitKey(500) if you're actually trying to display it). This is mostly due to the trackbars sucking, not the thresholding operation, which is not that slow.
You need to add your trackbars after you create the named window. Then, for your while loop, try:
while True:
# grab the frame
ret, frame = cap.read()
# get trackbar positions
ilowH = cv2.getTrackbarPos('lowH', 'image')
ihighH = cv2.getTrackbarPos('highH', 'image')
ilowS = cv2.getTrackbarPos('lowS', 'image')
ihighS = cv2.getTrackbarPos('highS', 'image')
ilowV = cv2.getTrackbarPos('lowV', 'image')
ihighV = cv2.getTrackbarPos('highV', 'image')
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_hsv = np.array([ilowH, ilowS, ilowV])
higher_hsv = np.array([ihighH, ihighS, ihighV])
mask = cv2.inRange(hsv, lower_hsv, higher_hsv)
frame = cv2.bitwise_and(frame, frame, mask=mask)
# show thresholded image
cv2.imshow('image', frame)
k = cv2.waitKey(1000) & 0xFF # large wait time to remove freezing
if k == 113 or k == 27:
break
and finally end the file with a cv2.destroyAllWindows()
As an aside, the maximum H value for HSV is 180, not 179.
Shameless plug: I happened to just finish a project doing precisely this, but on images. You can grab it on GitHub here. There is an example; try running it and then modifying as you need. It will let you change the colorspace and threshold inside each different colorspace, and it will print the final thresholding values that you ended on. Additionally it will return the output image from the operation for you to use, too. Hopefully it is useful for you! Feel free to send any issues or suggestions through GitHub for the project.
Here is an example of it running:
And as output it gives you:
Colorspace: HSV
Lower bound: [68.4, 0.0, 0.0]
Upper bound: [180.0, 255.0, 255.0]
as well as the binary image. I am currently working on getting this into a web application as well, but that probably won't be finished for a few days.
Use this code to find range of masking of real-time video! this might save you time. Below is a whole code, Check it and run it to have a test.
import cv2
import numpy as np
camera = cv2.VideoCapture(0)
def nothing(x):
pass
cv2.namedWindow('marking')
cv2.createTrackbar('H Lower','marking',0,179,nothing)
cv2.createTrackbar('H Higher','marking',179,179,nothing)
cv2.createTrackbar('S Lower','marking',0,255,nothing)
cv2.createTrackbar('S Higher','marking',255,255,nothing)
cv2.createTrackbar('V Lower','marking',0,255,nothing)
cv2.createTrackbar('V Higher','marking',255,255,nothing)
while(1):
_,img = camera.read()
img = cv2.flip(img,1)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hL = cv2.getTrackbarPos('H Lower','marking')
hH = cv2.getTrackbarPos('H Higher','marking')
sL = cv2.getTrackbarPos('S Lower','marking')
sH = cv2.getTrackbarPos('S Higher','marking')
vL = cv2.getTrackbarPos('V Lower','marking')
vH = cv2.getTrackbarPos('V Higher','marking')
LowerRegion = np.array([hL,sL,vL],np.uint8)
upperRegion = np.array([hH,sH,vH],np.uint8)
redObject = cv2.inRange(hsv,LowerRegion,upperRegion)
kernal = np.ones((1,1),"uint8")
red = cv2.morphologyEx(redObject,cv2.MORPH_OPEN,kernal)
red = cv2.dilate(red,kernal,iterations=1)
res1=cv2.bitwise_and(img, img, mask = red)
cv2.imshow("Masking ",res1)
if cv2.waitKey(10) & 0xFF == ord('q'):
camera.release()
cv2.destroyAllWindows()
break`
Thanks!
Hugs..
I do not want to save images from camera to disk so I am trying to pass the PIL image as a parameter like the code below
array = np.array(array)
im = Image.fromstring("RGB", (imageWidth, imageHeight), array)
return im
I want to access this from another function and do some image processing as below:
lower_blue = np.array([50, 0, 0])
upper_blue = np.array([255, 50, 50])
lower = np.array(lower_blue, dtype = "uint8")
upper = np.array(upper_blue, dtype = "uint8")
mask = cv2.inRange(image, lower, upper)
cv2.imwrite("mask.png",mask)
output = cv2.bitwise_and(image, image, mask = mask)
cv2.imwrite("output.png",output)
thresh = 90
gray = cv2.cvtColor(output,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,thresh,thresh*2)
cv2.imwrite("edges.png",edges)
I getan error "src is not a numpy array or a scalar"
How can I pass this as a parameter? If not, how can I pass the image into memory and then read from it.
I tried converting using np.array but all I get is a red image. The original image is not interpreted well.
I was able to make it work using the code below but is this the correct way to do it?
image = cv2.imdecode(np.fromstring(im, dtype='uint8'), cv2.IMREAD_UNCHANGED)
The following program displays 'foreground' completely black and not 'frame'. I also checked that all the values in 'frame' is equal to the values in 'foreground'.
They have same channels,data type etc.
I am using python 2.7.6 and OpenCV version 2.4.8
import cv2
import numpy as np
def subtractBackground(frame,background):
foreground = np.absolute(frame - background)
foreground = foreground >= 0
foreground = foreground.astype(int)
foreground = foreground * frame
cv2.imshow("foreground",foreground)
return foreground
def main():
cap = cv2.VideoCapture(0)
dump,background = cap.read()
while cap.isOpened():
dump,frame = cap.read()
frameCopy = subtractBackground(frame,background)
cv2.imshow('Live',frame)
k = cv2.waitKey(10)
if k == 32:
break
if __name__ == '__main__':
main()
Because you are telling OpenCV to display a 64bpc image. You cast .astype(int) which means 'int64' or 'int32' depending on your architecture. Cast .astype('uint8') instead. Your maximum brigthness of 255 looks black compared to the full 64bit range.
Related problem:
foreground = np.absolute(frame - background)
Integer underflow. The expression frame - background does not automatically convert to a signed data type. You need a different data type for such calculations (try float if performance doesn't matter), or find an OpenCV function that handles the whole thing for you.
foreground = foreground >= 0
Because foreground is of type 'uint8', which can never be negative, the result is all ones. Simply insert some print repr(foreground) statements to debug such problems.
You can use background sub tractor provided by opencv itself.
you can find the tutorial here.
for example look at the code
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2()
while(1):
ret, frame = cap.read()
fgmask = fgbg.apply(frame)
cv2.imshow('frame',fgmask)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
replace foreground = np.absolute(frame - background)
with foreground = cv2.absdiff(frame, background)