Denoise noisy straight lines / make noisy lines solid Python - python

I am attempting to denoise / make solid lines in a very noisy image of a floor-plan in python to no success. The methods I have used are:
masking
bluring
and houghlinesp
I have even tried a combination of the first two. here is the sample input image I am trying to make into solid straight lines:
With using the HoughLines method this is the best result I could achieve (lines solid but overlapping like crazy wherever there is text (This cannot easily be fixed by changing my minline/maxlinegap variables):
I have tried: masking, Gaussian blur, and Houghlinesp.
Houghlinesp Code:
import cv2
import numpy as np
from tkinter import Tk # from tkinter import Tk for Python 3.x
from tkinter.filedialog import askopenfilename
import os
Tk().withdraw() # we don't want a full GUI, so keep the root window from appearing
filename = askopenfilename() # show an "Open" dialog box and return the path to the selected file
print(filename)
filename3, file_extension = os.path.splitext(filename)
# Read input
img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
# Initialize output
out = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# Median blurring to get rid of the noise; invert image
img = 255 - cv2.medianBlur(img, 3)
# Detect and draw lines
lines = cv2.HoughLinesP(img, 1, np.pi/180, 10, minLineLength=40, maxLineGap=30)
for line in lines:
for x1, y1, x2, y2 in line:
cv2.line(out, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow('out', out)
cv2.imwrite(filename3+' '+'69'+'.png', out)
cv2.waitKey(0)
cv2.destroyAllWindows()

There are a few different things you could try, but to start I would recommend the following:
First, threshold the image to identify only the parts that constitute the floor plan
Next, dilate the image to connect any broken segments
Finally, erode the image to prevent your lines from being too thick
You'll have to mess around with the parameters to get it right, but I think this is your best bet to solve this problem without it getting too complicated.
If you want, you can also try the Sobel operators before thresholding to better identify horizontal and vertical lines.

Related

Detect dotted (broken) lines only in an image using OpenCV

I am trying to learn techniques on image feature detection.
I have managed to detect horizontal line(unbroken/continuous), however I am having trouble detecting all the dotted/broken lines in an image.
Here is my test image, as you can see there are dotted lines and some text/boxes etc.
So far I have used the following code which detected only one dotted line.
import cv2
import numpy as np
img=cv2.imread('test.jpg')
img=functions.image_resize(img,1000,1000) #function from a script to resize image to fit my screen
imgGray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
imgEdges=cv2.Canny(imgGray,100,250)
imgLines= cv2.HoughLinesP(imgEdges,2,np.pi/100,60, minLineLength = 10, maxLineGap = 100)
for x1,y1,x2,y2 in imgLines[0]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv2.imshow('Final Image with dotted Lines detected',img)
My output image is below. As you can see I only managed to detect the last dotted line. I have played around with the parameters rho,theta,min/max line but no luck.
Any advice is greatly appreciated :)
This solution:
import cv2
import numpy as np
img=cv2.imread('test.jpg')
kernel1 = np.ones((3,5),np.uint8)
kernel2 = np.ones((9,9),np.uint8)
imgGray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
imgBW=cv2.threshold(imgGray, 230, 255, cv2.THRESH_BINARY_INV)[1]
img1=cv2.erode(imgBW, kernel1, iterations=1)
img2=cv2.dilate(img1, kernel2, iterations=3)
img3 = cv2.bitwise_and(imgBW,img2)
img3= cv2.bitwise_not(img3)
img4 = cv2.bitwise_and(imgBW,imgBW,mask=img3)
imgLines= cv2.HoughLinesP(img4,15,np.pi/180,10, minLineLength = 440, maxLineGap = 15)
for i in range(len(imgLines)):
for x1,y1,x2,y2 in imgLines[i]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv2.imshow('Final Image with dotted Lines detected', img)
If you have an idea about the dot size, you can use black-hat transform to filter out the dotted lines. Black-hat is the difference between the closing of the image and the image. Then you can try hough line transform.
So, try
Convert bgr-to-gray
Apply black-hat using morphologyEx: this will leave only the black dots in the resulting image.
Invert the result and try hough line transform.
Here, you will have to experiment with the kernel size to filter only the dots. If that proves to be not very robust, another approach would be to use a blob detector. Invert the image and apply opencv blob detector or find contours. Filter the blobs/contours by area. Letters and other structures will have a larger area than the dots, so you can remove any structures that are larger than the dots. Then apply the hough line transform.
because you choose just one line to draw.
You change function draw line to
for i, line in enumerate(imgLines):
for x1, y1, x2, y2 in line:
cv2.line(img, (x1,y1), (x2,y2), (0,255,0), 2)
print(i, x1, y1, x2, y2)

How to remove white fuzziness from image in python

I'm trying to remove the background from product images, save them as transparent png's and got to a point where I can't figure out how and why I get the white line around the products like a fuzziness(see second image) don't know the real word for the effect. Also I'm losing the Nike swoosh which is white too :(
from PIL import Image
img = Image.open('test.jpg')
img = img.convert("RGBA")
datas = img.getdata()
newData = []
for item in datas:
if item[0] > 247 and item[1] > 247 and item[2] > 247:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
img.putdata(newData)
img.save("test.png", "PNG")
Any ideas how I can fix this so I get clean selections, edges ?
Take a copy of your image and use PIL/Pillow's ImageDraw.floodfill() to flood fill from the top-left corner using a reasonable tolerance - that way you will only fill to the edges of the shirt and avoid the Nike logo.
Then take the background outline and make it white and everything else black and try applying some morphology (from scikit-image maybe) to dilate the white a little larger to hide the jaggies.
Finally, put the resulting new layer into the image with putalpha().
I am really pushed for time, but here are the bones of it. Just missing the copy of the original image at the start and the putalpha() of the new alpha layer back at the end...
from PIL import Image, ImageDraw
import numpy as np
import skimage.morphology
# Open the shirt
im = Image.open('shirt.jpg')
# Make all background pixels (not including Nike logo) into magenta (255,0,255)
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=10)
# DEBUG
im.show()
Experiment with the threshold (thresh) here. If you make it 50, it works much more cleanly and may be good enough to stop.
# Make into Numpy array
n = np.array(im)
# Mask of magenta background pixels
bgMask =(n[:, :, 0:3] == [255,0,255]).all(2)
# DEBUG
Image.fromarray((bgMask*255).astype(np.uint8)).show()
# Make a disk-shaped structuring element
strel = skimage.morphology.disk(13)
# Perform a morphological closing with structuring element
closed = skimage.morphology.binary_closing(bgMask,selem=strel)
# DEBUG
Image.fromarray((closed*255).astype(np.uint8)).show()
If you are unfamiliar with morphology, Anthony Thyssen has some excellent noes worth reading here.
By the way, you could also use potrace to smooth the outline somewhat.
I had a bit more time today so here is a more complete version. You can experiment with the morphology disk sizes and floodfill thresholds according to your images till you find something tailored for your needs:
#!/bin/env python3
from PIL import Image, ImageDraw
import numpy as np
import skimage.morphology
# Open the shirt and make a clean copy before we dink with it too much
im = Image.open('shirt.jpg')
orig = im.copy()
# Make all background pixels (not including Nike logo) into magenta (255,0,255)
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=50)
# DEBUG
im.show()
# Make into Numpy array
n = np.array(im)
# Mask of magenta background pixels
bgMask =(n[:, :, 0:3] == [255,0,255]).all(2)
# DEBUG
Image.fromarray((bgMask*255).astype(np.uint8)).show()
# Make a disk-shaped structuring element
strel = skimage.morphology.disk(13)
# Perform a morphological closing with structuring element to remove blobs
newalpha = skimage.morphology.binary_closing(bgMask,selem=strel)
# Perform a morphological dilation to expand mask right to edges of shirt
newalpha = skimage.morphology.binary_dilation(newalpha, selem=strel)
# Make a PIL representation of newalpha, converting from True/False to 0/255
newalphaPIL = (newalpha*255).astype(np.uint8)
newalphaPIL = Image.fromarray(255-newalphaPIL, mode='L')
# DEBUG
newalphaPIL.show()
# Put new, cleaned up image into alpha layer of original image
orig.putalpha(newalphaPIL)
orig.save('result.png')
As regards using potrace to smooth the outline, you would save new alphaPIL as a PGM format image because that is what potrace likes as input. So that would be:
newalphaPIL.save('newalpha.pgm')
Now you can play around, oops I meant "experiment carefully" with potrace to smooth the alpha outline. The basic command is:
potrace -b pgm newalpha.pgm -o smoothalpha.pgm
You can then re-load the image smoothalpha.pgm back into your Python and use it on the last line in the putalpha() call. Here is an animation of the difference between the original unsmoothed alpha and the smoothed one:
Look carefully at the edges to see the difference. You may want to experiment with resizing the alpha either to twice the size or half the size before smoothing to see what effect that has.

How to erase the dotted watermark from set of similar images?

I want to automate the task of entering set of images into a number generating system & before that i like to remove a dotted watermark which is common across these images.
I tried using google, tesseract & abby reader, but I found that the image part that does not contain the watermark is recognized well, but the part that is watermarked is almost impossible to recognize.
I would like to remove the watermark using image processing. I already tried few sample codes of opencv, python, matlab etc but none matching my requirements...
Here is a sample code in Python that I tried which changes the brightness & darkness:
import cv2
import numpy as np
img = cv2.imread("d:\\Docs\\WFH_Work\\test.png")
alpha = 2.5
beta = -250
new = alpha * img + beta
new = np.clip(new, 0, 255).astype(np.uint8)
cv2.imshow("my window", new)
Unusually, i dont know the watermark of this image consists how many pixels. Is there a way to get rid of this watermark OR make digits dark and lower the darkness of watermark via code?
Here is watermarked image
I am using dilate to remove the figures, then find the edge to detect watermark. Remove it by main gray inside watermark
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.png', 0)
kernel = np.ones((10,10),np.uint8)
dilation = cv2.dilate(img,kernel,iterations = 1)
erosion = cv2.erode(dilation,kernel,iterations = 1)
plt.imshow(erosion, cmap='gray')
plt.show()
#contour
gray = cv2.bilateralFilter(erosion, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200)
plt.imshow(edged, cmap='gray')
plt.show()

OpenCV Python measuring distance with HoughLinesP() algorithm to determine water level

I'm trying to measure water level in a glass channel using OpenCV and Python. I've decided to use HaughLines in a selected ROI and find the midpoints of the said lines so I can calculate the difference between the ones that I want and multiply it with a set reference size that I'll get later on. So far the part where I find the lines look like this:
import cv2
import numpy as np
def midpoint(ptA, ptB, ptC, ptD):
return ((ptA + ptC) * 0.5, (ptB + ptD) * 0.5)
img = cv2.imread("b2924.JPG")
img = cv2.resize(img, None, fx=3/10, fy=3/10)
r = cv2.selectROI("main", img, False, False)
cropped = img[r[1]:(r[1]+r[3]), r[0]:(r[0]+r[2])]
cv2.destroyWindow("main")
imgray = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(imgray, 35, 75)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 75, maxLineGap=1000)
midPoint = []
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(cropped, (x1, y1), (x2, y2), (0, 0, 255), 1)
mP = midpoint(x1, y1, x2, y2)
midPoint.append(mP)
midPoint.sort(key = lambda x: x[1])
img[r[1]:(r[1]+r[3]), r[0]:(r[0]+r[2])] = cropped
print(lines)
print(midPoint)
cv2.imshow("img", img)
cv2.waitKey()
cv2.destroyAllWindows()
Depending on the image and the ROI I select I find inconsistent results. Image examples and where I select the ROIs:
Note that base of the channel starts where the duct tape reaches. It looks like I can almost never find that exact line because how noisy it is at the base. Right now these threshold values with no morphology seem to give the better results. I tried to use sobel derivative aswell instead of canny but got worse results.
Is it even possible to get exact measurements in this enviroment? Is it a matter of coding or changing the way I take the pictures or both? In the future I will possibly need to map the water profile during heavy turbulance, should I simply move away from OpenCV for that, since the noise is too much? Any help is appreciated.
I would not invest in any image processing with that setup.
If you insist on image processing (if you are only interested in the level at a few positions you might be better off using conventional level sensors)
Add LED panels or any other kind of homogeneous background illumination to the back of the basin. Add dye to the water to get some contrast.
Get rid of the window reflections. Clean the glass.
Alternatively make the background dark and add something to the water that makes it stray light or fluorescent.
You could also add stuff that floats on the surface and is either retroreflective or self-illuminated. That way you would get a bright surface level indicator that is easily detected in an image.

Dotted or dashed line with Python PILLOW

How to draw a dotted or dashed line or rectangle with Python PILLOW. Can anyone help me? Using openCV I can do that. But I want it using Pillow.
Thanks to #martineau's comment, I figured out how to draw a dotted line. Here is my code.
cur_x = 0
cur_y = 0
image_width = 600
for x in range(cur_x, image_width, 4):
draw.line([(x, cur_y), (x + 2, cur_y)], fill=(170, 170, 170))
This will draw a dotted line of gray color.
I decided to write up the idea I suggested in the comments - namely to draw the shapes with solid lines and then overlay a thresholded noisy image to obliterate parts of the line.
I made all the noise on a smaller image and then scaled it up so that the noise was "more clumped" instead of tiny blobs.
So this is just the generation of the test image:
#!/usr/local/bin/python3
import numpy as np
from PIL import Image, ImageDraw
# Make empty black image
im = Image.new('L', (640,480))
# Draw white rectangle and ellipse
draw = ImageDraw.Draw(im)
draw.rectangle([20,20,620,460],outline=255)
draw.ellipse([100,100,540,380],outline=255)
And this is generating the noise overlay and overlaying it - you can just delete this sentence and join the two lumps of code together:
# Make noisy overlay, 1/4 the size, threshold at 50%, scale up to full-size
noise = np.random.randint(0,256,(120,160),dtype=np.uint8)
noise = (noise>128)*255
noiseim = Image.fromarray(noise.astype(np.uint8))
noiseim = noiseim.resize((640,480), resample=Image.NEAREST)
# Paste the noise in, but only allowing the white shape outlines to be affected
im.paste(noiseim,mask=im)
im.save('result.png')
The result is this:
The solidly-drawn image is like this:
The noise is like this:
The following function draws a dashed line. It might be slow, but it works and I needed it.
"dashlen" is the length of the dashes, in pixels. -
"ratio" is the ratio of the empty space to the dash length (the higher the value the more empty space you get)
import math # math has the fastest sqrt
def linedashed(x0, y0, x1, y1, dashlen=4, ratio=3):
dx=x1-x0 # delta x
dy=y1-y0 # delta y
# check whether we can avoid sqrt
if dy==0: len=dx
elif dx==0: len=dy
else: len=math.sqrt(dx*dx+dy*dy) # length of line
xa=dx/len # x add for 1px line length
ya=dy/len # y add for 1px line length
step=dashlen*ratio # step to the next dash
a0=0
while a0<len:
a1=a0+dashlen
if a1>len: a1=len
draw.line((x0+xa*a0, y0+ya*a0, x0+xa*a1, y0+ya*a1), fill = (0,0,0))
a0+=step
I know this question is a bit old (4 y.o. at the time of my writing this answer), but as it happened I was in need of drawing a patterned line.
So I concocted my own solution here: https://codereview.stackexchange.com/questions/281582/algorithm-to-traverse-a-path-through-several-data-points-and-draw-a-patterned-li
(Sorry the solution was a bit long, better to just look there. The code works, though, that's why it's in CodeReview SE).
Provide the right "pattern dictionary", where blank segments are represented by setting color to None, and you should be good to go.

Categories

Resources