I have some cropped images and I need images that have black texts on white background. Firstly I apply adaptive thresholding and then I try to remove noise. Although I tried a lot of noise removal techniques but when the image changed, the techniques I used failed.
The best method for converting image color to binary for my images is Adaptive Gaussian Thresholding. Here is my code:
im_gray = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
image = cv2.GaussianBlur(im_gray, (5,5), 1)
th = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,3,2)
I need smooth values, Decimal separator(dot) and postfix letters. How can I do this?
Before binarization, it is necessary to correct the nonuniform illumination of the background. For example, like this:
import cv2
image = cv2.imread('9qBsB.jpg')
image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
se=cv2.getStructuringElement(cv2.MORPH_RECT , (8,8))
bg=cv2.morphologyEx(image, cv2.MORPH_DILATE, se)
out_gray=cv2.divide(image, bg, scale=255)
out_binary=cv2.threshold(out_gray, 0, 255, cv2.THRESH_OTSU )[1]
cv2.imshow('binary', out_binary)
cv2.imwrite('binary.png',out_binary)
cv2.imshow('gray', out_gray)
cv2.imwrite('gray.png',out_gray)
Result:
You can do slightly better using division normalization in Python/OpenCV.
Input:
import cv2
import numpy as np
# load image
img = cv2.imread("license_plate.jpg")
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# blur
blur = cv2.GaussianBlur(gray, (0,0), sigmaX=33, sigmaY=33)
# divide
divide = cv2.divide(gray, blur, scale=255)
# otsu threshold
thresh = cv2.threshold(divide, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# write result to disk
cv2.imwrite("hebrew_text_division.jpg", divide)
cv2.imwrite("hebrew_text_division_threshold.jpg", thresh)
cv2.imwrite("hebrew_text_division_morph.jpg", morph)
# display it
cv2.imshow("gray", gray)
cv2.imshow("divide", divide)
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.waitKey(0)
cv2.destroyAllWindows()
Division Image:
Thresholded Image:
Morphology Cleaned Image:
Im assuming that you are preprocessing the image for OCR(Optical Character Recognition)
I had a project to detect license plates and these were the steps I did, you can apply them to your project. After greying the image try applying equalize histogram to the image, this allows the area's in the image with lower contrast to gain a higher contrast. Then blur the image to reduce the noise in the background. Next apply edge detection on the image, make sure that noise is sufficiently removed as ED is susceptible to it. Lastly, apply closing(dilation then erosion) on the image to close all the small holes inside the words.
Instead of erode and dilate, you can check this, that is basically both in one.
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,2))
morphology_img = cv2.morphologyEx(img_grey, cv2.MORPH_OPEN, kernel,iterations=1)
plt.imshow(morphology_img,'Greys_r')
MORPHOLOGICAL_TRANSFORMATIONS
Related
I'm very new to OpenCV and recently, I'm trying to compare two images of rails, one with a train and one without. After the comparison, I apply a threshold, and there are some 'holes' in the white regions which I do not want. Currently, I am using dilation with 4 iterations and kernel set to "None", which defaults to a 3x3 by my understanding.
How do I decide what sort of kernel to use so that the dilation does a better job at making the white region continuous? Would also be nice if I could remove the small white blobs in the background. Here is the code:
resized = imutils.resize(img2, width=1050)
resized2 = imutils.resize(img3, width=1050)
grayA = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(resized2, cv2.COLOR_BGR2GRAY)
grayA = cv2.GaussianBlur(grayA,(7,7),0)
grayB = cv2.GaussianBlur(grayB,(7,7),0)
frameDelta = cv2.absdiff(grayA, grayB)
thresh = cv2.threshold(frameDelta, 20, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=4)
Complete beginner in this, so even general tips/advice to improve these comparisons would be vastly appreciated!
Perhaps this will give you some idea about morphology in Python/OpenCV. First I use a square "open" kernel about the size of the small white spots to remove them. Then I use a horizontal rectangle "close" kernel about the size of the black gap to fill it. "Open" removes white regions (or fills black gaps) and close removes black regions (or fills white gaps)
Input:
import cv2
import numpy as np
# read image as grayscale
img = cv2.imread('blob3.png', cv2.IMREAD_GRAYSCALE)
# threshold to binary
thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY)[1]
# apply morphology open with square kernel to remove small white spots
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (19,19))
morph1 = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# apply morphology close with horizontal rectangle kernel to fill horizontal gap
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (101,1))
morph2 = cv2.morphologyEx(morph1, cv2.MORPH_CLOSE, kernel)
# write results
cv2.imwrite("blob3_morph1.png", morph1)
cv2.imwrite("blob3_morph2.png", morph2)
# show results
cv2.imshow("thresh", thresh)
cv2.imshow("morph1", morph1)
cv2.imshow("morph2", morph2)
cv2.waitKey(0)
Morphology Square Open:
Morphology Rectangle Close:
Alternate Morphology Square Close:
I have built a code for Image Binarization and it works kind of well but the texts in my binary images either get too big or there's some white noise in them. What I want to do it to try Erosion, Dilation, Opening, Closing individually and then see which one is improving the results for me. Where should I use these morphological operations in my code. For example In Between Sharpened and binary image or between division and sharpened?
import numpy as np
import cv2
import skimage.filters as filters
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
smooth = cv2.GaussianBlur(gray, (93,93), 0,)
division = cv2.divide(gray, smooth, scale=255)
# kernel = np.ones((5,5),np.uint8) # use operations here
# opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
sharp = filters.unsharp_mask(division, radius=1.5, amount=1.5, multichannel=False, preserve_range=False)
sharp = (255*sharp).clip(0,255).astype(np.uint8)
# kernel = np.ones((5,5),np.uint8) # or here
# opening = cv2.morphologyEx(sharp, cv2.MORPH_OPEN, kernel)
thresh = cv2.threshold(sharp, 0, 255, cv2.THRESH_OTSU )[1]
I have the following retina image and I'm trying to draw a circle around the optic disk (the white round shape in retinal image). Here is the original image:
I applied adaptive thresholding then cv2.findcontour:
import cv2
def detectBlob(file):
# read image
img = cv2.imread(file)
imageName = file.split('.')[0]
# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 101, 3)
# apply morphology open then close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
blob = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20))
blob = cv2.morphologyEx(blob, cv2.MORPH_CLOSE, kernel)
# invert blob
blob = (255 - blob)
# Get contours
cnts,hierarchy = cv2.findContours(blob, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# write results to disk
result = img.copy()
cv2.drawContours(result, cnts, -1, (0, 0, 255), 3)
cv2.imwrite(imageName+"_threshold.jpg", thresh)
cv2.imwrite(imageName+"_blob.jpg", blob)
cv2.imwrite(imageName+"_contour.jpg", result)
detectBlob('16.png')
Here is the what the threshold looks like:
Here is the final output of contours:
Ideally I'm looking for such an output:
Adaptive thresholding fails because the filter size is much too small. And though we don't figure this out, the waves in the background are quite perturbating.
I obtained an interesting result by reducing the image resolution by a factor 16 and applying an adaptive filter of extent 99x99.
You need to identify larger structures. Ideally you need a structure size about 1/4 of the radius of the optic disk to balance results and processing time (experiment with larger sizes until acceptable).
Or you could downsample the image (reduce the resolution and make the picture smaller), which is more or less the same thing, even if you lose precision on the optic disk borders.
I have some black and white images of a single digit. I am using a NN model trained on MNIST to classify them. However, the digits are too rough and thick compared to the MNIST dataset. For example:
TLDR: I need to smoothen image and possibly make overall shape thinner using OpenCV.
You can use a combination of morphology close, open and erode (and optionally skeletonize and dilate) in Python/OpenCV as follows:
Input:
import cv2
import numpy as np
from skimage.morphology import skeletonize
# load image
img = cv2.imread("5.png")
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold image
thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)[1]
# apply morphology close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# apply morphology open
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# apply morphology erode
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (21,21))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_ERODE, kernel)
# write result to disk
cv2.imwrite("5_thinned.png", thresh)
# skeletonize image and dilate
skeleton = cv2.threshold(thresh,0,1,cv2.THRESH_BINARY)[1]
skeleton = (255*skeletonize(skeleton)).astype(np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
skeleton_dilated = cv2.morphologyEx(skeleton, cv2.MORPH_DILATE, kernel)
# write result to disk
cv2.imwrite("5_skeleton_dilated.png", skeleton_dilated)
cv2.imshow("IMAGE", img)
cv2.imshow("RESULT1", thresh)
cv2.imshow("RESULT2", skeleton_dilated)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result1 (close, open, erode):
Result2 (close, open, erode, skeletonize, dilate):
You will most likely benefit from morphological operations. Specifically it sounds like you want erosion.
You do have some noise though. You should try OpenCV's smoothing operations. Based on my experience, I think you need to use a median blur with a kernel area of maybe around 9 (although it depends on what you want). Then you need to use erode.
I'm using pytesseract to convert images into text, however the accuracy isn't 100% since the images pixelate on resizing. Applying gaussian blur would smoothen the edges but blur the image making it impossible for OCR to detect text.
What sort of filter would smoothen the edges without blurring the image too much. The image looks something like this
Image
You can median blur image then try a series of morphological transformations, specifically cv2.MORPH_CLOSE with a 3x3 kernel seems to work well here. You can play with the kernel size and number of iterations to get desired results
import cv2
image = cv2.imread('1.png')
blur = cv2.medianBlur(image, 7)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray,125, 255,cv2.THRESH_BINARY_INV)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
result = 255 - close
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.waitKey()