Removing the noise in edge detection - python

I was trying the detect and extract an image from the background (Example image here). Used canny edge detection and Hough transform for lines detection. But I'm getting wrong result due to noise in the image. An example image (Intermediate result) is shown here. Kindly help me to solve this problem in python.
I have used technique from below site and in addition to this used idea of Hough transform.
https://www.pyimagesearch.com/2014/04/21/building-pokedex-python-finding-game-boy-screen-step-4-6/

I hope this helps you:
import cv2
img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:, :, 2]
kern_size = 11
gray_blurred = cv2.medianBlur(gray, kern_size)
threshold_lower = 30
threshold_upper = 220
edged = cv2.Canny(gray_blurred, threshold_lower, threshold_upper)
cv2.imshow('edged',edged)
cv2.waitKey(0)

Related

OpenCV: Hough Circles, trouble detecting object

I am a complete beginner when it comes to OpenCV, I have no clue where to start when trying to detect circles of a certain size, below is my current code (not much) along with the image I am trying to detect, if anyone could help me or give me some advice it would be much appreciated, (i have converted image to grayscale and added gaussian blur so it is easier to detect) Thanks!
Image
import cv2
import numpy as np
test = cv2.imread('test.jpg')
gray_img = cv2.cvtColor(test, cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img, 5)
cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.imshow("HoughCirlces", test)
cv2.waitKey()
cv2.destroyAllWindows()
great work, you're almost there, all you have to do now is actually apply the CHT. The function you are looking for is cv2.HoughCircles(). You need to pass the image, in your case you can use either img or cimg and some parameters that might require tuning. Here is some template code
import cv2
import numpy as np
test = cv2.imread('test.jpg')
gray_img = cv2.cvtColor(test, cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img, 5)
cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow("HoughCirlces", test)
cv2.waitKey()
cv2.destroyAllWindows()
You can also take a look at the documentation and tutorial. I'll link them below.
Tutorial: https://docs.opencv.org/4.x/da/d53/tutorial_py_houghcircles.html
Docs: https://docs.opencv.org/4.x/dd/d1a/group__imgproc__feature.html#ga47849c3be0d0406ad3ca45db65a25d2d

OpenCV contour bumps detection

I have a following problem that I want to solve:
Paper cup is photographed from it's side and it's edges and contours are found with Canny and findContours function (please see IMG1 attached bellow), then I have to test if that contour is straight (without any bumps or other imprefections) and return if it's passed or not.
EDIT: as per Christoph Rackwitz recomendation I'm posting original non altered image.
I have successfully implemented edge detection as shown in IMG1 bellow, but now I want to remove parts of edges that are not used and want to get result that are shown in IMG2.
What would be the best way of doing it?
Few things that are on my mind:
Masks (mask off other paths)
Somehow delete paths that are radically different in x coordinates.
Yet another issue is detecting bumps on that curvy path. I'm thinking about few possible solutions:
Loop thru contours and look for spikes in x coordinates (radical changes, from 20 to 40 for examples)
draw similar radius arc and compare it with that contour.
The best result is shown in IMG3 image. Would be the perfect way of solving this problem for me.
Maybe there is someone who has more expierience with OPENCV and could light up my path to solving this problem. THANK YOU!
my code is as follow:
import numpy as np
import cv2
# Read the original image
img = cv2.imread('test2.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (3,3), 0)
edges = cv2.Canny(image=img_blur, threshold1=110, threshold2=180) # Canny Edge Detection
ret, thresh = cv2.threshold(edges, 110, 180, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# print(contours)
for cnt in contours:
print(cv2.arcLength(cnt, False))
image_copy = img.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(255, 0, 0), thickness=1, lineType=cv2.LINE_AA)
cv2.imshow('Binary image', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
IMG1 This is what is currently detected by OpenCV
IMG2 Good edge detection that I strive to achieve
IMG3 Perfect result with combined expected path and defect in one image
IMG4 Original image for testing and so on

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.

Detect text and deskew

Similar questions have been asked, but none of them seem to help in my case (nevertheless, I learned a few things from those threads).
I am using Tesseract for OCR, but the results are far from satisfactory when the text is slightly skewed (see the image above).
Inspired by similar cases, I tried to use OpenCV to detect and fix the skew, but unfortunately it just doesn't seem to work. Below, you can see my current attempt, which doesn't yield the necessary result. What I get is just another bounding box around the image (that has already been cropped).
import cv2
from matplotlib import pyplot as plt
import numpy as np
img = cv2.imread("skew.JPG")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#gray = cv2.bitwise_not(gray)
ret,thresh1 = cv2.threshold(gray, 0, 255 ,cv2.THRESH_OTSU)
rect_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3
, 2))
dilation = cv2.dilate(thresh1, rect_kernel, iterations = 1)
cv2.imshow('dilation', dilation)
cv2.waitKey(0)
cv2.destroyAllWindows()
contours, hierarchy = cv2.findContours(dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),3)
cv2.imshow('final', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I would appreciate any advice.
Tesseract seems to have a lot of troubles when the text has some distortions.
The idea is to find the contour of the text to be able to undistort the image and then use Tesseract.
The contour is generally a rectangle which has undergone the same distortion as the text. So it does not appear as a perfect rectangle in your image anymore. Opencv gives you different methods to find it. cv2.minAreaRect() finds the best rotated rectangle. It may be sufficient depending on the distortion of your text. Otherwise, you can use cv2.convexHull() to better fit your text.
The contour should give you the corners of the text that you want to remap to a regular rectangle. You can do that with:
cv2.getAffineTransform(corners, dest_corners) # requires 3 points
cv2.getPerspectiveTransform(corners, dest_corners) # requires 4 points
and then
cv2.warpAffine(...)
cv2.warpPerspective(...)
Also, don't forget to correctly set the page segmentation method that Tesseract needs to use (https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality). In your case, "6 Assume a single uniform block of text." seems to be adapted.

Categories

Resources