Trouble detecting numbers using pytesseract using a pixeled image - python

I am trying to detect/read the number in the image below. I apply a mask to the image (also shown below), and come back with a rather pixeled set of numbers. The only method for me to retrieve these numbers is there reading them from the image. any ideas on how to do that? I have tried pytesseract but it doesnt seem to work for the small/pixeled image I am using.
I am using Python 3.9, and CV2 (and pytesseract when I used it briefly). The code I used to apply the mask is also below.
upper_limit = np.array([0,255,255]) #Tried using a different lower limit, but all produced a black screen result besides this lower_limit = np.array([0,0,0]) mask = cv.inRange(image, lower_limit, upper_limit)
I also tried applying a Blur on the image and a Canny but the results were worse if anything.
original image
After Applying the mask

You want to threshold on white. White is (255,255,255). So this works fine for me in Python/OpenCV.
Input:
import cv2
import numpy as np
img = cv2.imread('text_921.png')
lower = (254,254,254)
upper = (255,255,255)
thresh = cv2.inRange(img, lower, upper)
cv2.imwrite('text_921_thesh.png', thresh)
cv2.imshow('thresh', thresh)
cv2.waitKey(0)
Result:

Related

How can I use thresholding to improve image quality after rotating an image with skimage.transform?

I have the following image:
Initial Image
I am using the following code the rotate the image:
from skimage.transform import rotate
image = cv2.imread('122.png')
rotated = rotate(image,34,cval=1,resize = True)
Once I execute this code, I receive the following image:
Rotated Image
To eliminate the blur on the image, I use the following code to set a threshold. Anything that is not white is turned to black (so the gray spots turn black). The code for that is as follows:
ret, thresh_hold = cv2.threshold(rotated, 0, 100, cv2.THRESH_BINARY)
plt.imshow(thresh_hold)
Instead of getting a nice clear picture, I receive the following:
Choppy Image
Does anyone know what I can do to improve the image quality, or adjust the threshold to create a clearer image?
I attempted to adjust the threshold to different values, but this changed the image to all black or all white.
One way to approach that is to simply antialias the image in Python/OpenCV.
To do that one simply converts to grayscale. Then blurs the image, then applies a stretch of the image.
Adjust the blur sigma to change the antialiasing.
Input:
import cv2
import numpy as np
import skimage.exposure
# load image
img = cv2.imread('122.png')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# blur threshold image
blur = cv2.GaussianBlur(gray, (0,0), sigmaX=2, sigmaY=2, borderType = cv2.BORDER_DEFAULT)
# stretch so that 255 -> 255 and 127.5 -> 0
result = skimage.exposure.rescale_intensity(blur, in_range=(127.5,255), out_range=(0,255)).astype(np.uint8)
# save output
cv2.imwrite('122_antialiased.png', result)
# Display various images to see the steps
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Why I could not use cv2.split result as masking for my cv2.bitwise_and?

I wonder why this is not work? I tried to do simple operation by splitting OpenCV logo to R,G,and B then I tried to apply the red mask to original image using bitwise and, but why I did not get only red part of the image? What I did wrong? Thank you.
Code Here:
It is most likely that your original sample is not "clean enough".
The values of R channel that looks black are close to zero but not zero.
When using cv2.bitwise_and or cv2.bitwise_or with a mask, all the values in the mask that are not equal to zero are taken as "True" (when mask != 0, the value is like 255).
A close inspection of the image you have posted unravels that the value of the black pixels is actually 1 and not 0:
I downloaded a "clean" OpenCV logo from here, and it's working as expected:
import cv2
import numpy as np
image = cv2.imread('OpenCV_Logo.png')
B, G, R = cv2.split(image)
# Using bitwise_or and bitwise_and gives the same result.
masked = cv2.bitwise_or(image, image, mask=R)
cv2.imshow('Red', R)
cv2.imshow('masked', masked)
cv2.waitKey()
cv2.destroyAllWindows()
OpenCV_Logo:
R:
masked:
As you can see there are leftovers around the edges, because the edges are not "pure" zeros.
Reproducing your problem is simple:
We may add 1 to all elements of image.
For avoiding overflow I used cv2.add instead of +1: image = cv2.add(image, np.ones_like(image).
Code sample:
import cv2
import numpy as np
image = cv2.imread('OpenCV_Logo.png')
image = cv2.add(image, np.ones_like(image))
B, G, R = cv2.split(image)
masked = cv2.bitwise_or(image, image, mask=R)
cv2.imshow('image', image)
cv2.imshow('Red', R)
cv2.imshow('masked', masked)
cv2.waitKey()
cv2.destroyAllWindows()
Result:
image:
R:
masked:
As you can see image and R looks the same, but masked result is completely different.
Suggested solutions:
You may find a better input image.
You may apply cv2.threshold to R for setting all the low values to zero.
cv2.threshold applies a threshold, the result is a binary image - all values are either zero or 255.
Example for using cv2.threshold:
import cv2
import numpy as np
image = cv2.imread('OpenCV_Logo.png')
image = cv2.add(image, np.ones_like(image)) # Modify the image for the example.
B, G, R = cv2.split(image)
# cv2.THRESH_OTSU finds the threshold automatically, you may use manual threshold instead.
R = cv2.threshold(R, 0, 255, cv2.THRESH_OTSU)[1]
masked = cv2.bitwise_or(image, image, mask=R)
cv2.imshow('image', image)
cv2.imshow('Red', R)
cv2.imshow('masked', masked)
cv2.waitKey()
cv2.destroyAllWindows()
Result:
masked:
What you're trying to do requires the cv2.bitwise_and() method, not the cv2.bitwise_or() method.
Try:
masked = cv2.bitwise_and(image, image, mask=R)
cv2.imshow('mask', masked)
See: OpenCV - Apply mask to a color image for reference.

Is there a way I can extract markers on a map image using python?

I have an image here, sample map
I wish to extract red lines on the map. The lines represent the number of times a user has chosen a path. The darker the color, the more times he chose that path.
Also, if I know the zoom level of the map (in this case 11), can I calculate the distance traveled for every color marker?
Thanks,
Try this code -
import numpy as np
import cv2
image = cv2.imread('test.jpg')
result = image.copy()
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([155,25,0])
upper = np.array([179,255,255])
mask = cv2.inRange(image, lower, upper)
result = cv2.bitwise_and(result, result, mask=mask)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.imwrite('output.jpg', result)
cv2.waitKey()
Output -

Processing image for reducing noise with OpenCV in Python

I want to apply some kind of preprocessing to this image so that text can be more readable, so that later I can read text from image. I'm new to this so I do not know what should I do, should I increase contrast or should I reduce noise, or something else. Basically, I want to remove these gray areas on the image and keep only black letters (as clear as they can be) and white background.
import cv2
img = cv2.imread('slika1.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', img)
cv2.waitKey(0)
thresh = 200
img = cv2.threshold(img, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imshow('filter',img)
cv2.waitKey(0)
I read the image and applied threshold to the image but I needed to try 20 different thresholds until I found one that gives results.
Is there any better way to solve problems like this?
The problem is that I can get different pictures with different size of gray areas, so sometime I do not need to apply any kind of threshold, and sometimes I do, because of that I think that my solution with threshold is not that good.
For this image, my code works good:
But for this it gives terrible results:
Try division normalization in Python/OpenCV. Divide the input by its blurred copy. Then sharpen. You may want to crop the receipt better or mask out the background first.
Input:
import cv2
import numpy as np
import skimage.filters as filters
# read the image
img = cv2.imread('receipt2.jpg')
# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# blur
smooth = cv2.GaussianBlur(gray, (95,95), 0)
# divide gray by morphology image
division = cv2.divide(gray, smooth, scale=255)
# sharpen using unsharp masking
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)
# save results
cv2.imwrite('receipt2_division.png',division)
cv2.imwrite('receipt2_division_sharp.png',sharp)
# show results
cv2.imshow('smooth', smooth)
cv2.imshow('division', division)
cv2.imshow('sharp', sharp)
cv2.waitKey(0)
cv2.destroyAllWindows()
Division result:
Sharpened result:

How to auto adjust contrast and brightness of a scanned Image with opencv python

I want to auto adjust the brightness and contrast of a color image taken from phone under different lighting conditions. Please help me I am new to OpenCV.
Source:
Input Image
Result:
result
What I am looking for is more of a localized transformation. In essence, I want the shadow to get as light as possible completely gone if possible and get darker pixels of the image to get darker, more in contrast and the light pixels to get more white but not to a point where it gets overexposed or anything like that.
I have tried CLAHE, Histogram Equalization, Binary Thresholding, Adaptive Thresholding, etc But nothing has worked.
My initials thoughts are that I need to neutralize Highlights and bring darker pixels more towards the average value while keeping the text and lines as dark as possible. And then maybe do a contrast filter. But I am unable to Get the result please help me.
Here is one way to do that in Python/OpenCV.
Read the input
Increase contrast
Convert original to grayscale
Adaptive threshold
Use the thresholded image to make the background white on the contrast increased image
Save results
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("math_diagram.jpg")
# 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, 21, 15)
# make background of input white where thresh is white
result = img.copy()
result[thresh==255] = (255,255,255)
# write results to disk
cv2.imwrite("math_diagram_threshold.jpg", thresh)
cv2.imwrite("math_diagram_processed.jpg", result)
# display it
cv2.imshow("THRESHOLD", thresh)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Threshold image:
Result:
You can use any local binarization method. In OpenCV there is one such method called Wolf-Julion local binarization which can be applied to the input image. Below is code snippet as an example:
import cv2
image = cv2.imread('input.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)[:,:,2]
T = cv2.ximgproc.niBlackThreshold(gray, maxValue=255, type=cv2.THRESH_BINARY_INV, blockSize=81, k=0.1, binarizationMethod=cv2.ximgproc.BINARIZATION_WOLF)
grayb = (gray > T).astype("uint8") * 255
cv2.imshow("Binary", grayb)
cv2.waitKey(0)
The output result from above code is below. Please note that to use ximgproc module you need to install opencv contrib package.

Categories

Resources