Blur from OpenCV in Region of Interest - python

I am trying create a circle and blur the contents in OpenCV. However, I am able to make the circle, but I am not able to blur that part. My code is given below. Please help me out
import io
import picamera
import cv2
import numpy as np
import glob
from time import sleep
from PIL import ImageFilter
image = cv2.imread('/home/pi/Desktop/cricle-test/output_0020.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faceCascade = cv2.CascadeClassifier('/home/pi/Desktop/Image-Detection-test/haarcascade_frontalface_alt.xml')
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.2,
minNeighbors=5,
minSize=(30, 30),
flags = cv2.cv.CV_HAAR_SCALE_IMAGE
)
print "Found {0} faces!".format(len(faces))
# Draw a circle around the faces and blur
for (x, y, w, h) in faces:
sub = cv2.circle(image, ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (0, 255, 0), 5)
cv2.blur(image(x,y,w,h),(23,23), 40000)
cv2.imwrite("/home/pi/Desktop/cricle-test/output_0020.jpg" ,image)

For it to work you need to do a couple of things, first cv2.blur needs a destination and not a number. This can be achieved with:
image[y:y+h, x:x+w] = cv2.blur(image[y:y+h, x:x+w] ,(23,23))
Since you are saving the image to the same file in every loop, you can just save it after the loop.
Since you wanted a circular bur, you need to create a circular mask, then apply it to the image, here is how your code will look like (only the loop part):
# create a temp image and a mask to work on
tempImg = image.copy()
maskShape = (image.shape[0], image.shape[1], 1)
mask = np.full(maskShape, 0, dtype=np.uint8)
# start the face loop
for (x, y, w, h) in faces:
#blur first so that the circle is not blurred
tempImg [y:y+h, x:x+w] = cv2.blur(tempImg [y:y+h, x:x+w] ,(23,23))
# create the circle in the mask and in the tempImg, notice the one in the mask is full
cv2.circle(tempImg , ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (0, 255, 0), 5)
cv2.circle(mask , ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (255), -1)
# oustide of the loop, apply the mask and save
mask_inv = cv2.bitwise_not(mask)
img1_bg = cv2.bitwise_and(image,image,mask = mask_inv)
img2_fg = cv2.bitwise_and(tempImg,tempImg,mask = mask)
dst = cv2.add(img1_bg,img2_fg)
cv2.imwrite("/home/pi/Desktop/cricle-test/output_0020.jpg" ,dst)
This seems to work, at least in my test, you may try adjusting the kernel size (this (23,23) in the blur) to get less or more blurred image, for example, try this code with (7,7) it will have more details.
Update
If you want to use ellipses instead of circles just change the circle instruction to:
cv2.ellipse(mask , ( ( int((x + x + w )/2), int((y + y + h)/2 )),(w,h), 0), 255, -1)
The same way you can change it to a rectangle, polygon, or any other shape.

Related

How can ı blur detected object (YoloV5)? [duplicate]

I am trying create a circle and blur the contents in OpenCV. However, I am able to make the circle, but I am not able to blur that part. My code is given below. Please help me out
import io
import picamera
import cv2
import numpy as np
import glob
from time import sleep
from PIL import ImageFilter
image = cv2.imread('/home/pi/Desktop/cricle-test/output_0020.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faceCascade = cv2.CascadeClassifier('/home/pi/Desktop/Image-Detection-test/haarcascade_frontalface_alt.xml')
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.2,
minNeighbors=5,
minSize=(30, 30),
flags = cv2.cv.CV_HAAR_SCALE_IMAGE
)
print "Found {0} faces!".format(len(faces))
# Draw a circle around the faces and blur
for (x, y, w, h) in faces:
sub = cv2.circle(image, ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (0, 255, 0), 5)
cv2.blur(image(x,y,w,h),(23,23), 40000)
cv2.imwrite("/home/pi/Desktop/cricle-test/output_0020.jpg" ,image)
For it to work you need to do a couple of things, first cv2.blur needs a destination and not a number. This can be achieved with:
image[y:y+h, x:x+w] = cv2.blur(image[y:y+h, x:x+w] ,(23,23))
Since you are saving the image to the same file in every loop, you can just save it after the loop.
Since you wanted a circular bur, you need to create a circular mask, then apply it to the image, here is how your code will look like (only the loop part):
# create a temp image and a mask to work on
tempImg = image.copy()
maskShape = (image.shape[0], image.shape[1], 1)
mask = np.full(maskShape, 0, dtype=np.uint8)
# start the face loop
for (x, y, w, h) in faces:
#blur first so that the circle is not blurred
tempImg [y:y+h, x:x+w] = cv2.blur(tempImg [y:y+h, x:x+w] ,(23,23))
# create the circle in the mask and in the tempImg, notice the one in the mask is full
cv2.circle(tempImg , ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (0, 255, 0), 5)
cv2.circle(mask , ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (255), -1)
# oustide of the loop, apply the mask and save
mask_inv = cv2.bitwise_not(mask)
img1_bg = cv2.bitwise_and(image,image,mask = mask_inv)
img2_fg = cv2.bitwise_and(tempImg,tempImg,mask = mask)
dst = cv2.add(img1_bg,img2_fg)
cv2.imwrite("/home/pi/Desktop/cricle-test/output_0020.jpg" ,dst)
This seems to work, at least in my test, you may try adjusting the kernel size (this (23,23) in the blur) to get less or more blurred image, for example, try this code with (7,7) it will have more details.
Update
If you want to use ellipses instead of circles just change the circle instruction to:
cv2.ellipse(mask , ( ( int((x + x + w )/2), int((y + y + h)/2 )),(w,h), 0), 255, -1)
The same way you can change it to a rectangle, polygon, or any other shape.

Reliable program that can detect QR Codes without cv2.QRCodeDetector() or pyzbar library

I am having trouble finding a set of morphological operations that allow me to detect (only) the QR codes in various images using cv2.connectedComponentsWithStats() or cv2.findContours() (but I would prefer to solve this with cv2.connectedComponentsWithStats()).
The images I absolutely need the code to work on are the following:
I have been messing with 2 different codes, one using cv2.connectedComponentsWithStats() and the other cv2.findContours() and some other methods (based off nathancy's answer to Detect a QR code from an image and crop using OpenCV). To test I've been using the following codes:
Using cv2.connectedComponentsWithStats(), the problem with this code is that it captures more than the QR code in the 2nd as you can see bellow. In the 1st it works great and in the 3rd as well if scaled to 0.5, or else it also detects more than the QR code like the 2nd image.
import cv2
import numpy as np
#img = cv2.imread('Code-1.jpg'); scale = 1;
img = cv2.imread('Code-2.jpg'); scale = 1;
#img = cv2.imread('Code-3.jpg'); scale = 0.5;
width = int(img.shape[1] * scale); height = int(img.shape[0] * scale); img = cv2.resize(img, (width, height))
og = img.copy()
gray = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gaussianblur = cv2.GaussianBlur(gray, (7,7), 0)
otsuthresh = cv2.threshold(gaussianblur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
edges = cv2.Canny(otsuthresh, threshold1=100, threshold2=200)
dilate = cv2.dilate(edges,(5,5),iterations=1)
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(dilate, 8, cv2.CV_32S)
for i in range(1,num_labels):
objint = (labels == i).astype(np.uint8)*255/i
x = stats[i, cv2.CC_STAT_LEFT]
y = stats[i, cv2.CC_STAT_TOP]
w = stats[i, cv2.CC_STAT_WIDTH]
h = stats[i, cv2.CC_STAT_HEIGHT]
area = stats[i, cv2.CC_STAT_AREA]
ratio = w / float(h)
(cX, cY) = centroids[i]
if area > 500 and (ratio > .95 and ratio < 1.05) and (w < 0.99*img.shape[1]):
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
ROI = og[y:y + h, x:x + w]
cv2.imwrite('ROI.png', ROI)
cv2.imshow('image', img)
cv2.imshow('QR code', ROI)
Using cv2.findContours(), this one can't detect any of the QR codes in the images in which the code must not fail, but can detect in some other random images
import cv2
import numpy as np
#img = cv2.imread('Code-1.jpg'); scale = 1;
img = cv2.imread('Code-2.jpg'); scale = 1;
#img = cv2.imread('Code-3.jpg'); scale = 0.5;
width = int(img.shape[1] * scale); height = int(img.shape[0] * scale); img = cv2.resize(img, (width, height))
og = img.copy()
gray = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gaussianblur = cv2.GaussianBlur(gray, (7,7), 0)
otsuthresh = cv2.threshold(gaussianblur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
closed = cv2.morphologyEx(otsuthresh, cv2.MORPH_CLOSE, kernel, iterations=3)
contours = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) == 2:
contours = contours[0]
else:
contours = contours[1]
for cnt in contours:
perim = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.05 * perim, True)
x,y,w,h = cv2.boundingRect(approx)
area = cv2.contourArea(cnt)
ratio = w / float(h)
if len(approx) == 4 and area > 1000 and (ratio > .80 and ratio < 1.2):
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 4)
ROI = og[y:y + h, x:x + w]
cv2.imwrite('ROI.png', ROI)
cv2.imshow('image', img)
cv2.imshow('QR code', ROI)
Thank you for reading and if I wasn't clear on something please let me know.
Filipe Almeida
Maybe, you could try QReader. It is just a wrapper of OpenCV, Pyzbar and other QR detection and image filtering methods, but it works quite out-of-the-box for those cases.
from qreader import QReader
from matplotlib import pyplot as plt
import cv2
if __name__ == '__main__':
# Initialize QReader
detector = QReader()
for img_path in ('0oOAF.jpg', 'HXlS8.jpg', '5fFTo.jpg'):
# Read the image
img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
# Detect the QR bbox
found, bbox = detector.detect(image=img)
if found:
# Draw the bbox
x1, y1, x2, y2 = bbox
cv2.rectangle(img=img, pt1=(x1, y1), pt2=(x2, y2), color=(0, 255, 0), thickness=2)
# Save the image
plt.imshow(img)
plt.savefig(f"{img_path}-bbox.png")
That's the output it gives:

How can I detect circle by using openCV in python?

I want to detect circle in a picture by using haar cascade. I created an cascade xml.
import cv2
import numpy as np
img = cv2.imread("C://OpenCVcascade//resimler//coins.jpg")
circles_cascade = cv2.CascadeClassifier("C://Cascade//dairetanima.xml")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = circles_cascade.detectMultiScale(gray, 1.1, 1)
if circles is not None:
circles = np.uint16(np.around(circles))
for (x, y, w, h) in circles:
center = (x + w // 2, y + h // 2)
radius = (w + h) // 4
cv2.circle(img, center, radius, (255, 0, 0), 2)
cv2.imshow('image', img)
cv2.waitKey()
cv2.destroyAllWindows()
My result:
I already know, there are different method to detect circle. But I am trying to do with cascade method. Because after this part, I will use it for real time detection.

How to blur a face in opencv with round borders - Python?

I blurred the face in OpenCV like this:
I used this code:
face = cv2.medianBlur(face, 100)
img[top:bottom,left:right] = face
But I want to make the face border round like this (does not need to be perfect)
First, create a mask image. To do so, draw a white circle at the face location on a black image.
Second, blur the whole image.
Third, copy blurred content to the original image only where your mask is > 0.
p1 = (65, 65)
w, h = 100, 100
p2 = (p1[0] + w, p1[1] + h)
circle_center = ((p1[0] + p2[0])// 2, (p1[1] + p2[1]) // 2)
circle_radius = int(math.sqrt(w * w + h * h) // 2)
mask_img = np.zeros(img.shape, dtype='uint8')
cv2.circle(mask_img, circle_center, circle_radius, (255, 255, 255), -1)
img_all_blurred = cv2.medianBlur(img, 99)
img_face_blurred = np.where(mask_img > 0, img_all_blurred, img)
Output:
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('image.jpg')
h, w, c = img.shape
plt.imshow(img)
plt.show()
c_mask = np.zeros((h,w), np.uint8)
cv2.circle(c_mask,(w//2,h//2),100,1,thickness=-1)
mask = cv2.bitwise_and(img, img, mask=c_mask)
plt.imshow(mask)
plt.show()
img_mask = img - mask
plt.imshow(img_mask)
plt.show()
blur = cv2.blur(img,(17, 17))
plt.imshow(blur)
plt.show()
mask2 = cv2.bitwise_and(blur, blur, mask=c_mask) # mask
plt.imshow(mask2)
plt.show()
final_img = img_mask + mask2
print(np.max(final_img))
plt.imshow(final_img)
plt.show()
You can blur whole image then copy the result to source using any mask you like.

Find dominant color on contour opencv

I am trying to find the dominant color inside a contour (black or white).
I am using OpenCV to read an image and extract white on black images. This is what I got so far:
The green outline is the contour, the blue lines the bounding box. So I this instance I am trying to extract the numbers 87575220 but as you can see it also recognizes some random artifacts and for instance the letter G. I think the solution would be to find the dominant colour inside of the contours and that colour should be close to white. I don't have any idea how to do this though.
This the code I have at the moment:
import argparse
import cv2
import imutils
import numpy as np
parser = argparse.ArgumentParser()
parser.add_argument("--image", "-i", required=True, help="Image to detect blobs from")
args = vars(parser.parse_args())
image = cv2.imread(args["image"])
image = imutils.resize(image, width=1200)
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(grey)
maxval_10 = maxVal * 0.5
ret, threshold = cv2.threshold(grey, maxval_10, 255, cv2.THRESH_BINARY)
canny = cv2.Canny(grey, 200, 250)
lines = cv2.HoughLines(canny, 1, np.pi / 180, 140)
print(maxVal)
theta_min = 60 * np.pi / 180.
theta_max = 120 * np.pi / 180.0
theta_avr = 0
theta_deg = 0
filteredLines = []
for rho, theta in lines[0]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
if theta_min <= theta <= theta_max:
filteredLines.append(theta)
theta_avr += theta
if len(filteredLines) > 0:
theta_avr /= len(filteredLines)
theta_deg = (theta_avr / np.pi * 180) - 90
else:
print("Failed to detect skew")
image = imutils.rotate(image, theta_deg)
canny = imutils.rotate(canny, theta_deg)
im2, contours, hierarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# cv2.drawContours(image, contours, -1, (0, 255, 0), 1)
cv2.imshow('Contours', im2)
boundingBoxes = []
filteredContours = []
for cnt in contours:
(x, y, w, h) = cv2.boundingRect(cnt)
if (h > 20 and h < 90 and w > 5 and w < h):
if cv2.contourArea(cnt, True) <= 0:
boundingBoxes.append((x, y, w, h))
filteredContours.append(cnt)
for x, y, w, h in boundingBoxes:
cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.drawContours(image, filteredContours, -1, (0, 255, 0), 1)
cv2.imshow('Image', image)
cv2.imshow('Edges', canny)
cv2.imshow('Threshold', threshold)
cv2.waitKey(0)
cv2.destroyAllWindows()
This is the original picture:
I would try to make a ROI before I start searching for numbers. You have not give the original image so this example is made with the image you posted (with boxes and contours allready drawn). Should aslo work with the original though. Steps are written in the example code. Hope it helps. Cheers!
Example code:
import cv2
import numpy as np
# Read the image and make a copy then create a blank mask
img = cv2.imread('dominant.jpg')
img2 = img.copy()
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)
# Transform to gray colorspace and perform histogram equalization
gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
equ = cv2.equalizeHist(gray)
# Transform all pixels above thershold to white
black = np.where(equ>10)
img2[black[0], black[1], :] = [255, 255, 255]
# Transform to gray colorspace and make a thershold then dilate the thershold
gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((15,15),np.uint8)
dilation = cv2.dilate(thresh,kernel,iterations = 1)
# Search for contours and select the biggest one and draw it on mask
_, contours, hierarchy = cv2.findContours(dilation,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(mask, [cnt], 0, 255, -1)
# Perform a bitwise operation
res = cv2.bitwise_and(img, img, mask=mask)
# Display the ROI
cv2.imshow('img', res)
Result:
You could create a mask out of each contour:
mask = np.zeros(image.shape, dtype="uint8")
cv2.drawContours(mask, [cnt], -1, 255, -1)
and then calculate the mean value of all pixels inside of the mask:
mean = cv2.mean(image, mask=mask)
and then check whether mean is close enough to white
Colors and mean do not match well due to color space properties. I would create an histogram and select the most frequent one (some color down sampling could be applied too)

Categories

Resources