How to set white pixels to transparent using OpenCV - python-3.x

My code so far. I want to crop out the white in the image.
import cv2
import numpy as np
image = cv2.imread('myimage.jpg')
image2 = np.ones((255, 255, 4))
for i in range(255):
for j in range(255):
if image[i,j,0] == 255:
image2[i, j, :] = np.append(image[i, j, :], 1)
else:
image2[i, j, :] = np.append(image[i, j, :], 1)
cv2.imwrite('image2.png', image2)
But it just produces an empty plot.

This should be enough:
The background of the website is white, so "right click" the Output and "open image in new tab" and you'll see that it is transparent :)
import cv2
import numpy as np
# read the image
image_bgr = cv2.imread('image_bgr.png')
# get the image dimensions (height, width and channels)
h, w, c = image_bgr.shape
# append Alpha channel -- required for BGRA (Blue, Green, Red, Alpha)
image_bgra = np.concatenate([image_bgr, np.full((h, w, 1), 255, dtype=np.uint8)], axis=-1)
# create a mask where white pixels ([255, 255, 255]) are True
white = np.all(image_bgr == [255, 255, 255], axis=-1)
# change the values of Alpha to 0 for all the white pixels
image_bgra[white, -1] = 0
# save the image
cv2.imwrite('image_bgra.png', image_bgra)
Input:
Output ("right click" >> "open in new tab"):

Related

Masking colors using opencv

I tried to mask image by its color using opencv.
import cv2
import numpy as np
import matplotlib.pyplot as plt
After importing libraries, I load the image
img = cv2.imread('gmaps.jpg')
image = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(image);
Turn the color into hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
plt.imshow(hsv);
Masking process
low_orange = np.array([44, 6, 100])
high_orange = np.array([44, 24, 99])
masking = cv2.inRange(hsv,low_orange, high_orange)
plt.imshow(masking);
The result isn't what I expected.
Image :
Result :
EDIT: I want to mask the building only. Instead I got the result of masking all of the frame.
Using my answer from here I manage to extract the right values for you
Code:
frame = cv2.imread("Xv6gx.png")
blurred_frame = cv2.GaussianBlur(frame, (5, 5), 0)
hsv = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
lower = np.array([4, 0, 7])
upper = np.array([87, 240, 255])
mask = cv2.inRange(hsv, lower, upper)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for contour in contours:
area = cv2.contourArea(contour)
if area > 5000:
# -- Draw Option 1 --
cv2.drawContours(frame, contour, -1, (0, 255, 0), 3)
# -- Draw Option 2--
# rect = cv2.boundingRect(contour)
# x, y, w, h = rect
# cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Mask", mask)
cv2.imshow("Frame", frame)
cv2.waitKey(0)
Final Results:
I wouldn't expect the low Value (100) to exceed the high Value (99).
Also, OpenCV uses a range of 0..180 for Hue rather than 0..360, so you likely need to divide your 44 by 2.

How to crop images based on mask threshold?

I have to crop a lot of images manually. Not the funniest thing to do. So I thought I'd try to do it using Python.
I can detect the subject, create a mask, but I have no idea how to get the points from the very bottom part and crop based on them.
Any help is appreciated
import cv2
img = cv2.imread('image5.jpg')
h, w = img.shape[:2]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.threshold(gray, 192, 255, cv2.THRESH_BINARY_INV)[1]
cv2.imwrite('result5.png', thr)
you can try to find all external contours using cv2.RETR_EXTERNAL and pick the bottom most point, like this:
import cv2
import numpy as np
import imutils
im = cv2.imread('images/tennis.jpg')
# Percent of original size
scale_percent = 20
width = int(im.shape[1] * scale_percent / 100)
height = int(im.shape[0] * scale_percent / 100)
dim = (width, height)
# Resize image
im = cv2.resize(im, dim, interpolation = cv2.INTER_AREA)
# Convert to grayscale
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# Canny
canny_output = cv2.Canny(im, 120, 240)
# Find external contours
contours, hierarchy = cv2.findContours(canny_output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(im, [contours[0]], 0, (0,255,0), 3) # Uncomment this line to see what contour opencv is finding
# Pick the bottom most point and add an offset (whatever value you want, this is just for aesthetics)
c = contours[0]
bottommost = tuple(c[c[:, :, 1].argmax()][0])[1] + 5
# Crop image
im = im[:bottommost, :]
# Show image
cv2.imshow('image', im)
cv2.waitKey()
Very good thinking I'd say! now the implementation:
xx,yy = thrs.nonzero()
max_crop_h = xx.max()
crop = img[:max_crop_h,:]
numpy has your back!

Python Extract number from simple Image

I have the following image
lower = np.array([175, 125, 45], dtype="uint8")
upper = np.array([255, 255, 255], dtype="uint8")
mask = cv2.inRange(image, lower, upper)
img = cv2.bitwise_and(image, image, mask=mask)
plt.figure()
plt.imshow(img)
plt.axis('off')
plt.show()
now if I try to transform into grayscale like this:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
I get that:
And I would like to extract the number on it.
The suggestion:
gray = 255 - gray
emp = np.full_like(gray, 255)
emp -= gray
emp[emp==0] = 255
emp[emp<100] = 0
gauss = cv2.GaussianBlur(emp, (3,3), 1)
gauss[gauss<220] = 0
plt.imshow(gauss)
gives the image:
Then using pytesseract on any of the images:
data = pytesseract.image_to_string(img, config='outputbase digits')
gives:
'\x0c'
Another suggested solution is:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
thr = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV)[1]
txt = pytesseract.image_to_string(thr)
plt.imshow(thr)
And this gives
'\x0c'
Not very satisfying... Anyone has a better solution please?
Thanks!
I have a two step solution
Apply thresholding
Set psm mode to 7.
When you apply thresholding to the image:
Thresholding is a simplest method of displaying the features of the image.
Now from the output image, when we read:
txt = image_to_string(thr, config="--psm 7")
print(txt)
Result will be:
| 1,625 |
Now why do we set page-segmentation-mode (psm) mode to the 7?
Well, treating image as a single text line will give the accurate result.
But we have to modify the result. Since the current result is | 1,625 |
We should remove the |
print("".join([t for t in txt if t != '|']))
Result:
1,625
Code:
import cv2
from pytesseract import image_to_string
img = cv2.imread("LZ3vi.png")
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.threshold(gry, 0, 255,
cv2.THRESH_BINARY_INV)[1]
txt = image_to_string(thr, config="--psm 7")
print("".join([t for t in txt if t != '|']).strip())
Update
how do you get this clean black and white image from my original image?
Using 3-steps
Reading the image using opencv's imread function
img = cv2.imread("LZ3vi.png")
Now we read the image in BGR fashion. (Not RGB)
Convert the image to the graysclae
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
Result will be:
Apply threshold
thr = cv2.threshold(gry, 0, 255, cv2.THRESH_BINARY_INV)[1]
Result will be:
Now if you are wondering about thresholding. Read the simple-threhsolding
All my filters, grayscale... get weird colored images
The reason is, when you are displaying the image using pyplot, you need to set color-map (cmap) to gray
plt.imshow(img, cmap='gray')
You can read the other types here
Two issues blocked the pytessract from detecting your number:
The white rectangle around the number(Inverting and filling is the solution).
The Noise in the numbers shape(Gaussian Smoothing dealt with that)
The solution that AlexAlex has proposed will work perfectly if it was followed by a Gaussian filter:
output: 1,625
import numpy as np
import pytesseract
import cv2
BGR = cv2.imread('11.png')
RGB = cv2.cvtColor(BGR, cv2.COLOR_BGR2RGB)
lower = np.array([175, 125, 45], dtype="uint8")
upper = np.array([255, 255, 255], dtype="uint8")
mask = cv2.inRange(RGB, lower, upper)
img = cv2.bitwise_and(RGB, RGB, mask=mask)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray = 255 - gray
emp = np.full_like(gray, 255)
emp -= gray
emp[emp==0] = 255
emp[emp<100] = 0
gauss = cv2.GaussianBlur(emp, (3,3), 1)
gauss[gauss<220] = 0
text = pytesseract.image_to_string(gauss, config='outputbase digits')
print(text)

How to detect and recognize complex shapes in the image using Python Image Processing?

I am new to StackOverflow.
I am trying to detecting shapes as per names and shapes in image of diamonds.various shapes of diamond. For shape detection purpose I used the program given at https://www.pyimagesearch.com/2016/02/08/opencv-shape-detection/
Here is the code:
import cv2
import imutils
class ShapeDetector:
def __init__(self):
pass
def detect(self, c):
# initialize the shape name and approximate the contour
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
# if the shape is a triangle, it will have 3 vertices
if len(approx) == 3:
shape = "triangle"
# if the shape has 4 vertices, it is either a square or
# a rectangle
elif len(approx) == 4:
# compute the bounding box of the contour and use the
# bounding box to compute the aspect ratio
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
# a square will have an aspect ratio that is approximately
# equal to one, otherwise, the shape is a rectangle
shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
# if the shape is a pentagon, it will have 5 vertices
elif len(approx) == 5:
shape = "pentagon"
# otherwise, we assume the shape is a circle
else:
shape = "circle"
# return the name of the shape
return shape
# -----------------
image = cv2.imread(shape_image)
resized = imutils.resize(image, width=300)
ratio = image.shape[0] / float(resized.shape[0])
gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 20, 255, cv2.THRESH_BINARY)[1]
# find contours in the thresholded image and initialize the
# shape detector
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
sd = ShapeDetector()
# loop over the contours
for c in cnts:
# compute the center of the contour, then detect the name of the
# shape using only the contour
M = cv2.moments(c)
cX = int((M["m10"] / M["m00"]) * ratio)
cY = int((M["m01"] / M["m00"]) * ratio)
shape = sd.detect(c)
# multiply the contour (x, y)-coordinates by the resize ratio,
# then draw the contours and the name of the shape on the image
c = c.astype("float")
c *= ratio
c = c.astype("int")
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
print("The Shape of Object is: ", shape)
# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
But, I am unable to get perfect results for that. Is there any way to find out shapes like which are given in image.

apply heat map to the opencv rectangle

iam trying to apply a heat map on a selected portion[people's face] of my picture. Here is what i had done so far...
the rectangle will be applied to the face .
the face will be cropped
heat map will be applied to the cropped image.
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
crop_img = image[y:y+h, x:x+w]
# Cropping Area
# Color Mapping Area
images = cv2.imread(crop_img, 0)
colormap = plt.get_cmap('inferno')
heatmap = (colormap(images) * 2**16).astype(np.uint16)[:,:,:3]
heatmap = cv2.cvtColor(heatmap, cv2.COLOR_RGB2BGR)
# Saving Color Map
img_names = "heatimage{}.png".format(i)
cv2.imwrite(img_names, heatmap)
print("{} written!".format(img_names))
img = cv2.imread(img_names,0)
cv2.imshow('heatmap{}'.format(i),heatmap)
i was able to save the cropped image and rectangle pointing faces separately but i need
1. to make the rectangle to be a heat map ,without cropping separately,in my original image.
2. other part of the image has to be normal
EDITED
Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
crop_img = image[y:y+h, x:x+w]
sample = cv2.imread("sample.jpg",cv2.COLOR_BGR2GRAY)
colormap = cm.get_cmap('inferno', 256)
cmp = cm.ScalarMappable(cmap='inferno')
# create 1D float gradient from 0 to 1 with 256 increments
# convert to rgba in range 0 to 255 (via bytes=True)
# remove alpha channel and reshape to 256x1 3 channel from (256, 4)
# convert rgb to bgr
cmap = np.linspace(0, 1, 256, endpoint=True)
cmap = cmp.to_rgba(cmap, bytes=True)
cmap = cmap[:, 0:-1].reshape((256, 1, 3))
cmap = cv2.cvtColor(cmap, cv2.COLOR_RGB2BGR)
# apply color map to crop
crop_mapped = cv2.applyColorMap(crop_img, cmap)
# put color mapped crop back into input
result = sample.copy()
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
result[y:y+h, x:x+w] = crop_mapped
# save result
cv2.imwrite('IRimage.jpg', result)
# show result
cv2.imshow("result", result)
i+=1
cv2.imshow("Faces found", image)
If i have more than one face , how can i apply color filter to both the faces?
If I understand correctly, you're trying to display the portion of a heatmap that's on the face of a person.Try this
alpha = 0.5
image[y:y+h, x:x+w] = alpha * image[y:y+h, x:x+w] + (1 - alpha) * heatmap[y:y+h, x:x+w]
cv2.imshow("preview", image)
I believe that you will have to crop the image, apply the colormap to the cropped image, then put the color mapped crop image back into your original. I do not think there is a way to apply a colormap directly to a portion of an image.
Here is how I do the above in Python/OpenCV.
Read the input as grayscale
Crop the image where you want it to be color mapped
Load the colormap from Matplotlib and convert it to a BGR image
Apply the colormap to the cropped image
Convert the input to 3 channel gray and insert the color mapped cropped image back in the correct location.
Save the results
Input:
import cv2
import numpy as np
import matplotlib.cm as cm
# read image and convert to gray
img = cv2.imread('redhat_gray.jpg', cv2.COLOR_BGR2GRAY)
# crop image
crop = img[140:240, 70:170]
# get colormap from matplotlib and normalize
colormap = cm.get_cmap('inferno', 256)
cmp = cm.ScalarMappable(cmap='inferno')
# create 1D float gradient from 0 to 1 with 256 increments
# convert to rgba in range 0 to 255 (via bytes=True)
# remove alpha channel and reshape to 256x1 3 channel from (256, 4)
# convert rgb to bgr
cmap = np.linspace(0, 1, 256, endpoint=True)
cmap = cmp.to_rgba(cmap, bytes=True)
cmap = cmap[:, 0:-1].reshape((256, 1, 3))
cmap = cv2.cvtColor(cmap, cv2.COLOR_RGB2BGR)
#print(cmap)
# apply color map to crop
crop_mapped = cv2.applyColorMap(crop, cmap)
# put color mapped crop back into input
result = img.copy()
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
result[140:240, 70:170] = crop_mapped
# save result
cv2.imwrite('redhat_gray_rectangle_inferno.jpg', result)
# show result
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Resources