I am applying filter. By using OpenCv live webcam is open and it detect face and apply filter. But i want to crop the face in circle and removed the extra background and save the image.
for example:
to this
How can i implement in python?
The idea is to create a black mask then draw the desired region to crop out in white using cv2.circle(). From there we can use cv2.bitwise_and() with the original image and the mask. To crop the result, we can use cv2.boundingRect() on the mask to obtain the ROI then use Numpy slicing to extract the result. For this example I used the center point as (335, 245). You can adjust the circle radius to increase or decrease the size of the circle.
Code
import cv2
import numpy as np
# Create mask and draw circle onto mask
image = cv2.imread('1.jpg')
mask = np.zeros(image.shape, dtype=np.uint8)
x,y = 335, 245
cv2.circle(mask, (x,y), 110, (255,255,255), -1)
# Bitwise-and for ROI
ROI = cv2.bitwise_and(image, mask)
# Crop mask and turn background white
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
x,y,w,h = cv2.boundingRect(mask)
result = ROI[y:y+h,x:x+w]
mask = mask[y:y+h,x:x+w]
result[mask==0] = (255,255,255)
cv2.imshow('result', result)
cv2.waitKey()
Related
I have this image.
I want to make all the colored headings to white and the text in it to black.
I try below to make the image full black and white.
img_grey = cv2.imread('img.jpg', cv2.IMREAD_GRAYSCALE)
thresh = 170
img_binary = cv2.threshold(img_grey, thresh, 250, cv2.THRESH_BINARY)[1]
cv2.imwrite('bw_img.jpg',img_binary)
Now those headings are black and text within those is white. But i want to make text black and heading layout white. So, can anyone helps me with that?
You may convert the image to HSV, apply threshold to find colored regions, and copy the result of cv2.threshold with cv2.THRESH_BINARY_INV only to the colored regions.
Main stages of the suggested solution:
Convert from BGR to HSV color space, and get the saturation color channel.
all black and white are zero, and colored pixels are above zero.
Apply threshold to the saturation channel.
Find contours on the binarized saturation channel.
Draw the contours as white (255 values) on black background to form a mask.
Apply morphological closing for closing some black gaps.
Get only the area inside the mask from img_binary_inv (result of your code using cv2.THRESH_BINARY_INV).
Copy masked img_binary_inv to img_grey only in pixels that mask is white.
Complete code sample:
import numpy as np
import cv2
img_bgr = cv2.imread('img.jpg') # Read image as BGR
img_grey = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # Convert from BGR to grayscale.
thresh = 170
img_binary_inv = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY_INV)[1] # Apply threshold and invert black/white
# Convert from BGR to HSV color space.
hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# Get the saturation color channel - all black and white are zero, and colored pixels are above zero.
s = hsv[:, :, 1]
thresh = 100
s_binary = cv2.threshold(s, thresh, 255, cv2.THRESH_BINARY)[1] # Apply threshold to the saturation channel.
# Find contours on s_binary
cnts = cv2.findContours(s_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2] # Use index [-2] to be compatible to OpenCV 3 and 4
# Draw the contours as white (255 values) on black background.
mask = np.zeros_like(s_binary)
cv2.drawContours(mask, cnts, -1, 255, -1)
# Apply morphological closing for closing some black gaps.
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((3, 3)))
# Get only the area inside the mask from img_binary_inv
masked_binary_inv = cv2.bitwise_or(img_binary_inv, img_binary_inv, mask=mask)
# Copy masked_binary_inv to img_grey only in pixels that mask is white.
cv2.copyTo(masked_binary_inv, mask, img_grey)
cv2.imwrite('img_grey.png', img_grey) # Save result (as PNG and not JPEG for better quality).
# Show images
cv2.imshow('img_bgr', img_bgr)
cv2.imshow('s_binary', s_binary)
cv2.imshow('mask', mask)
cv2.imshow('masked_binary_inv', masked_binary_inv)
cv2.imshow('img_grey', img_grey)
cv2.waitKey()
cv2.destroyAllWindows()
Result (img_grey):
The result doesn't look so good, due to the relatively low quality of the input image.
Intermediate results:
s_binary:
mask:
masked_binary_inv:
I want to remove a background from x-ray to extract actual area. So My original
image looks like left image and I want to crop to look like image.
A solution with Python and Open-CV is appreciated.
There are multiple files and so we don't know the height and width to. Crop in advance. So it needs to be computed.
Here is one way to do that in Python/OpenCV.
Read the input
Convert to gray
Threshold
Blacken the bottom two white rows
Find where all white pixels are in the image
Get the bounds of those pixels
Crop the image at the bounds
Save the results
Input:
import cv2
import numpy as np
# load image as grayscale
img = cv2.imread('xray_chest.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
hh, ww = thresh.shape
# make bottom 2 rows black where they are white the full width of the image
thresh[hh-3:hh, 0:ww] = 0
# get bounds of white pixels
white = np.where(thresh==255)
xmin, ymin, xmax, ymax = np.min(white[1]), np.min(white[0]), np.max(white[1]), np.max(white[0])
print(xmin,xmax,ymin,ymax)
# crop the image at the bounds adding back the two blackened rows at the bottom
crop = img[ymin:ymax+3, xmin:xmax]
# save resulting masked image
cv2.imwrite('xray_chest_thresh.jpg', thresh)
cv2.imwrite('xray_chest_crop.jpg', crop)
# display result
cv2.imshow("thresh", thresh)
cv2.imshow("crop", crop)
cv2.waitKey(0)
cv2.destroyAllWindows()
Thresholded image with bottom two rows blackened:
Cropped input:
An alternate method would be to get the external contour of the white region from the thresholded image. Get the bounds of the contour. Then crop to those bounds.
I want to crop a circle area of an image (ROI) and put it onto a white mask with same dimensions and positions.
Build a white mask:
h, w = img.shape[:2]
mask = np.ones((h,w,3), np.uint8)*255
The circle area (ROI) in the image (--> this is wrong, because it is only a circle and I want an area with all pixels in the circle of this and copy it in the white mask):
roi = cv2.circle(img, (656,517), 505, (0,0,0), -1)
How I can copy all the pixels in the circle of the image onto a mask with same dimensions and the same position of the circle?
The image has the dimensions: 1280x1024.
Basically, you just need to use OpenCV's bitwise_and method properly, i.e. you need an additional mask for the circular ROI. Also, you need two bitwise_and calls, since you want to maintain the white background in the final image.
import cv2
import numpy as np
# Original image
image = cv2.imread('path/to/your/image.png')
cv2.imshow('image', image)
# Circular ROI in original image; must be selected via an additional mask
roi = np.zeros(image.shape[:2], np.uint8)
roi = cv2.circle(roi, (175, 125), 100, 255, cv2.FILLED)
cv2.imshow('roi', roi)
# Target image; white background
mask = np.ones_like(image) * 255
cv2.imshow('mask before operation', mask)
# Copy ROI part from original image to target image
mask = cv2.bitwise_and(mask, image, mask=roi) + cv2.bitwise_and(mask, mask, mask=~roi)
cv2.imshow('mask after operation', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
Some input image:
Generated output:
Hope that helps!
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.8.1
NumPy: 1.18.1
OpenCV: 4.1.2
----------------------------------------
I have an image like this:
And I want to crop the image anywhere there is red.
So with this image I would be looking to produce 4 crops:
Obviously I first need to detect anywhere there is red in the image. I can do the following:
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
## (1) Read and convert to HSV
img = cv2.imread("my_image_with_red.png")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## (2) Find the target red region in HSV
hsv_lower = np.array([0,50,50])
hsv_upper = np.array([10,255,255])
mask = cv2.inRange(hsv, hsv_lower, hsv_upper)
## (3) morph-op to remove horizone lines
kernel = np.ones((5,1), np.uint8)
mask2 = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
## (4) crop the region
ys, xs = np.nonzero(mask2)
ymin, ymax = ys.min(), ys.max()
xmin, xmax = xs.min(), xs.max()
croped = img[ymin:ymax, xmin:xmax]
pts = np.int32([[xmin, ymin],[xmin,ymax],[xmax,ymax],[xmax,ymin]])
cv2.drawContours(img, [pts], -1, (0,255,0), 1, cv2.LINE_AA)
cv2_imshow(croped)
cv2_imshow(img)
cv2.waitKey()
Which gives the following result:
The bounding box covers the entire area containing red.
How can I get bounding boxes around each red piece of the image? I have looked into multiple masks but this doesn't seem to work.
What I am looking for is:
detect each red spot in the image;
return boundaries on each red dot;
use those boundaries to produce 4 individual crops as new images.
There are currently several problems:
If you look at your mask image, you will see that all traces of red are captured on the mask including the small noise. You're currently using np.nonzero() which captures all white pixels. This is what causes the bounding box to cover the entire area. To fix this, we can tighten up the lower hsv threshold to get this resulting mask:
Note there are still a lot of small blobs. Your question should be rephrased to
How can I crop the large red regions?
If you want to capture all red regions, you will obtain much more then 4 crops. So to remedy this, we will perform morphological operations to remove the small noise and keep only the large pronounced red regions. This results in a mask image that contains the large regions
You do not require multiple masks
How can I get bounding boxes around each red piece of the image?
You can do this using cv2.findContours() on the mask image to return the bounding rectangles of each red dot.
Oh? This is not your desired result. Since your desired result has some space surrounding each red dot, we also need to include a offset to the bounding rectangle. After adding an offset, here's our result
Since we have the bounding rectangles, we can simply use Numpy slicing to extract and save each ROI. Here's the saved ROIs
So to recap, to detect each red spot in the image, we can use HSV color thresholding. Note this will return all pixels which match this threshold which may be different from what you expect so it is necessary to perform morphological operations to filter the resulting mask. To obtain the bounding rectangles on each red blob, we can use cv2.findContours() which will give us the ROIs using cv2.boundingRect(). Once we have the ROI, we add a offset and extract the ROI using Numpy slicing.
import cv2
import numpy as np
image = cv2.imread("1.png")
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hsv_lower = np.array([0,150,50])
hsv_upper = np.array([10,255,255])
mask = cv2.inRange(hsv, hsv_lower, hsv_upper)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=1)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
offset = 20
ROI_number = 0
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(image, (x - offset, y - offset), (x + w + offset, y + h + offset), (36,255,12), 2)
ROI = original[y-offset:y+h+offset, x-offset:x+w+offset]
cv2.imwrite('ROI_{}.png'.format(ROI_number), ROI)
ROI_number += 1
cv2.imshow('mask', mask)
cv2.imshow('close', close)
cv2.imshow('image', image)
cv2.waitKey()
I am using Opencv and python to detect shapes and then crop them. I have succeeded to do that, however now I am trying to take the cropped images and remove their backgrounds.
The image has a circle inside and surrounded by gray color. (It can be gray or can be even more than one color).
How can I remove the colors surrounding the circle border (which is black) - we can convert the gray color to black - as the border color or even remove it at all and make that transparent.
The result image should contain only the circle.
At least in for this image, there is no need to detect the circle use houghCircle). I think threshold it and find the inner contour , then make mask and do bitwise-op is OK!
My steps:
(1) Read and convert to gray
(2) findContours
(3) find contour that smaller, create a mask
(4) do bitwise_and to crop
Here is my result:
#!/usr/bin/python3
# 2018.01.20 20:58:12 CST
# 2018.01.20 21:24:29 CST
import cv2
import numpy as np
## (1) Read
img = cv2.imread("img04.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## (2) Threshold
th, threshed = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)
## (3) Find the min-area contour
_cnts = cv2.findContours(threshed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2]
cnts = sorted(cnts, key=cv2.contourArea)
for cnt in cnts:
if cv2.contourArea(cnt) > 100:
break
## (4) Create mask and do bitwise-op
mask = np.zeros(img.shape[:2],np.uint8)
cv2.drawContours(mask, [cnt],-1, 255, -1)
dst = cv2.bitwise_and(img, img, mask=mask)
## Save it
cv2.imwrite("dst.png", dst)