OpenCV : How to clear Image Background of ID Card without losing quality - python-3.x

I want to clear an image background of ID without losing quality, Keep only the text with white background
Using the following code is not efficient, produce high noise and distortion
img = cv2.imread(imge)
# Convert into grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of black color in HSV
lower_val = np.array([0,0,0])
upper_val = np.array([179,255,135])
# Threshold the HSV image to get only black colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# invert mask to get black symbols on white background
mask_inv1 = cv2.bitwise_not(mask)
mask_inv = cv2.blur(mask_inv1,(5,5))
How I can achieve clean background with these images
Samples
Output

Using Rembg to get perfect output like below
Rembg is an offline tool to remove images background. you can simply install Rembg package using pip in python
pip install rembg
afterthat,
rembg i path/to/input.png path/to/output.png

Related

How to convert a grayscale image's black to alpha "correctly"?

I made a quick script to convert a grayscale image's black to alpha, and it looks like this:
from PIL import Image
i = Image.open("glow.jpg")
new_i = i.convert("RGBA")
pxs = new_i.load()
# The original image's size is 1024x1024
for x in range(1024):
for y in range(1024):
pxs[x, y] = (255, 255, 255, pxs[x,y][0])
new_i.save("glow.png")
After executing the script i tried to blend the resulting png with a black background in GIMP, and it is different from the original one:
Is it a problem with GIMP? If not, how can I modify the code to make it blend correctly?

Python Image/Pillow: How to make the background more white of images

I have images like these:
What I would like to do is to make the background of the image more white so that the letters are more visible. Here is a good image from my perspective:
I am using Pillow in Python. Thank you in advance!
The simplest is probably to use ImageOps.autocontrast() to increase the contrast like this:
from PIL import Image, ImageOps
# Open image as greyscale
im = Image.open('letter.png').convert('L')
# Autocontrast
result = ImageOps.autocontrast(im)
# Save
result.save('result.png')
A more sophisticated approach would be to use an Otsu thresholding to split the pixels optimally into 2 colours, but for that you would want scikit-image like this:
from skimage import filters
from skimage.io import imread, imsave
# Load image as greyscale
img = imread('letter.png', as_gray=True)
# Get Otsu threshold - result is 151
threshold = filters.threshold_otsu(img)
You can now either continue and make all pixels above the threshold white and leave those below as they are:
img[img>threshold] = 255
imsave('result.png',img)
Or, you can do a complete threshold where all pixels end up either solid black or solid white:
result = (img>threshold).astype(np.uint8) * 255
imsave('result.png',result)

Opencv plot black pixels on a image

I have a input image similar to
I am referring to:
How to fill the gaps in letters after Canny edge detection
I want to plot black pixels on this image. The proposed solution on the above url is first find all black pixels using
import matplotlib.pyplot as pp
import numpy as np
image = pp.imread(r'/home/cris/tmp/Zuv3p.jpg')
bin = np.all(image<100, axis=2)
My question is dow do I plot this black pixels (data stored in bin ) on image while ignoring all other colour channels.
In the answer is stated that np.all(image<100, axis=2) is used to select pixels where R,G and B are all lower then 100, which is basically color separation. Personally, I like to use the HSV-colorspace for that.
Result:
Note: if you want to improve the green letters, it is best to create a separate mask for that, and tweak the hsv values for green.
Code:
import numpy as np
import cv2
# load image
img = cv2.imread("img.jpg")
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of black color in HSV
lower_val = np.array([0,0,0])
upper_val = np.array([179,255,127])
# Threshold the HSV image to get only black colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# invert mask to get black symbols on white background
mask_inv = cv2.bitwise_not(mask)
# display image
cv2.imshow("Mask", mask_inv)
cv2.imshow("Img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Converting PNG PIL Image into OpenCV Image replaces transparency with black background

When I try to convert PNG type PIL Image into OpenCV Image, transparent background at PNG turns into black background. How can I keep the transparent background in OpenCV Image object.
Here is the code piece :
# PIL Image object which holds a transparent background png image.
pil_img = Image.open(ioFile).convert('RGBA')
pil_img.show()
# I use numpy to convert the pil_image into a numpy array
numpy_image = np.array(pil_img)
# I convert to a openCV2 image, notice the COLOR_RGB2BGR which means that
# the color is converted from RGBA to BGR format
opencvImage = cv2.cvtColor(numpy_image, cv2.COLOR_RGBA2BGRA)
#
#(I commented below lines, to show that I tried them but did not work.)
#
# opencvImage = cv2.cvtColor(numpy_image, cv2.IMREAD_UNCHANGED)
# opencvImage = cv2.cvtColor(numpy_image, cv2.COLOR_RGB2BGR)
showImage(opencvImage)
The last line of code piece shows an image with black background. I probably choose the wrong convert method and, could not find the proper one.
You can use this code for save transparency when converting.
To convert (with Alpha) from Pillow image to OpenCv image:
You can manually change the color order.
import cv2
from PIL import Image
import numpy as np
pillowImage = Image.open('picturePath.png').convert('RGBA')
img = np.array(pillowImage) # 'img' Color order: RGBA
red = img[:,:,0].copy() # Copy R from RGBA
img[:,:,0] = img[:,:,2].copy() # Copy B to first order. Color order: BGBA
img[:,:,2] = red # Copy R to the third order. Color order: BGRA
opencvImage = img # img is OpenCV variable

Extracting text OpenCV Contours

I tried doing ocr of each individual contour using tesseract but not getting proper text out of it. Contour Identification is done properly by using Extracting text OpenCV.
Please suggest.
You are not getting proper text from OCR because of bad image pre-processing.
Try various image processing techniques to narrow down on a workable approach for your image.
As you have asked under python, If you have a colour image,
Convert it into black and white image, to remove the colour noise.
img = cv2.imread('name_of_the_coloured_input_image',0)
Blur the image using blurring techniques of opencv (averaging, gaussian blurring, median blurring and bilateral filtering), this decreases various noises in the image.
Please refer to this link and try out various techniques
Then use thresholding (simple, adaptive or otsu thresholding), which removes all the pixels which are less than a certain threshold.
Please refer to this link and try out various techniques
Now, get contours and try using tesseract on the contours to get better results.
Note : Please remember that for tesseract to work, you should have the text in black against a white background.
Please check for the below function, tell me if anything is missing.
#gray out the image
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', gray)
cv2.waitKey(0)
#image blurring
blur = cv2.blur(gray,(1,1))
cv2.imshow('Blur', blur)
cv2.waitKey(0)
#threshold & invert
ret, thresh = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY_INV)
thresh_copy = thresh.copy()
cv2.imshow("Threshold", thresh_copy)
cv2.waitKey(0)
#Erosion
kernel1 = np.ones((1,1), np.uint8)
img_erosion = cv2.erode(thresh, kernel1, iterations=1)
cv2.imshow("Erosion", img_erosion.copy())
cv2.waitKey(0)
#applying dilation
kernel = np.ones((6,10), np.uint8)
img_dilation = cv2.dilate(img_erosion.copy(), kernel, iterations=1)
cv2.imshow("Dilation", img_dilation)
cv2.waitKey(0)
#find contours
im2, ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return ctrs

Resources