How to fill holes or reduce noise in an image? - python-3.x

I need to reduce the noise in images like the one bellow, i.e. fill the holes in the white object. I tried something with opencv but it ended up removing part of the object as you can see. Is there a better way to do this without losing the object itself? Any help is appreciated!
Here's what I have so far:
import numpy as np
import cv2
def remove_noise(gray, num):
Y, X = gray.shape
nearest_neigbours = [[
np.argmax(
np.bincount(
gray[max(i - num, 0):min(i + num, Y), max(j - num, 0):min(j + num, X)].ravel()))
for j in range(X)] for i in range(Y)]
result = np.array(nearest_neigbours, dtype=np.uint8)
cv2.imwrite('result.png', result)
return result
img = cv2.imread('img.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
remove_noise(gray, 10)
Input image:
Output image:

Following #JeruLuke's suggestion, I used cv.morphologyEx(img, cv.MORPH_CLOSE, kernel) and got the result I wanted with the following code snippet.
import cv2
import numpy as np
image = cv2.imread('image.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
kernel_size = (7, 7)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
closing = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel)
cv2.imwrite('result.png', closing)
Output image:

Related

How to create image sequence?

My problem is extracting the text from multiple columns of .PDF.
Common libs like PyPDF2 didn't work.
The code below I made to try to read with Pytesseract but I was also unsuccessful because it is mixing the two columns.
Now my idea using this code as a base is to create a cutout in each column 1 and 2 and generate a new image by pasting column 1 and then columns 2 below, so I could read with Pytesseract or AWS Textract without problems.
how could i do this with opencv?
import fitz
import cv2
import pytesseract
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
SCANNED_FILE = "decreto_santos.pdf"
img = cv2.imread(SCANNED_FILE)
zoom_x = 2.0
zoom_y = 2.0
mat = fitz.Matrix(zoom_x, zoom_y)
# I create an image for each page of the PDF and save.
doc = fitz.open(SCANNED_FILE)
print("Generated pages: ")
for page in doc:
pix = page.get_pixmap(matriz=mat)
png = 'output/' + SCANNED_FILE.split('/')[-1].split('.')[0] + 'page-%i.png' % page.number
print(png)
pix.save(png)
# Upload an image to crop
original_image = cv2.imread('output/decreto_santospage-1.png')
# Grayscale image
gray_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
plt.figure(figsize=(25, 15))
plt.imshow(gray_image, cmap='gray')
plt.show()
# Result:
# Otsu thresholding
ret, threshold_image = cv2.threshold(gray_image, 0,255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
plt.figure(figsize=(25, 15))
plt.imshow(threshold_image, cmap='gray')
plt.show()
# Result:
rectangular_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# Applying dilation on the threshold image
dilated_image = cv2.dilate(threshold_image, rectangular_kernel, iterations = 1)
plt.figure(figsize=(25, 15))
plt.imshow(dilated_image)
plt.show()
# Result:
# Finding contours
contours, hierarchy = cv2.findContours(dilated_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Creating a copy of the image
copied_image = original_image.copy()
with open("output/recognized-kernel-66-66.txt", "w+") as f:
f.write("")
f.close()
mask = np.zeros(original_image.shape, np.uint8)
# Looping through the identified contours
# Then rectangular part is cropped and passed on to pytesseract
# pytesseract extracts the text inside each contours
# Extracted text is then written into a text file
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
# Cropping the text block for giving input to OCR
cropped = copied_image[y:y + h, x:x + w]
with open("output/recognized-kernel-66-66.txt", "a") as f:
# Apply OCR on the cropped image
text = pytesseract.image_to_string(cropped, lang='por', config='--oem 1 --psm 1')
print(text)
masked = cv2.drawContours(mask, [cnt], 0, (255, 255, 255), -1)
plt.figure(figsize=(25, 15))
plt.imshow(masked, cmap='gray')
plt.show()
My base for this code was this post

Opencv findCountours function

I'm trying to learn opencv. Online i found that, with opencv, I can obtain the contours of some image. So i tried that. Here is the script:
import cv2
import numpy as np
def getC(imagine):
global imgContour
c,h = cv2.findContours(imagine,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for cnt in c:
a = cv2.contourArea(cnt)
print(area)
if area>500:
cv2.drawContour(imgContour,cnt,-1,(255,0,0),3)
img = cv2.imread("a3.jpg")
imgContour = img.copy()
imgG = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgB = cv2.GaussianBlur(imgG,(7,7),1)
imgC = cv2.Canny(imgB,50,50)
getC(imgContour)
cv2.imshow("",img)
cv2.imshow("g",imgG)
cv2.imshow("b",imgB)
cv2.imshow("l",imgContour)
cv2.waitKey(0)
I think there is a problem with global variabiles, but also with the format. a3.jpg is that image.
I don't now what to do now, and how to resolve the issue.
Thanks for the help
you saved the area as the variable a but used it with the name area you can fix this by changing the variable name a to area
area = cv2.contourArea(cnt)
there is a typo in cv2.drawContour you should write it like that cv2.drawContours
cv2.drawContours method expects the contour you want to draw to be a list of lists so you need to call it like that
cv2.drawContours(imgContour,[cnt],-1,(255,0,0),3)
when you pass the image to the getC method you gave it an image without pre-processing this image and converting it to threshold image using canny so you need to call it like that
getC(imgC)
The Final Script
import cv2
import numpy as np
def getC(imagine):
global imgContour
print(imgContour.shape)
c,h = cv2.findContours(imagine,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for cnt in c:
area = cv2.contourArea(cnt)
print(area)
if area>500:
cv2.drawContours(imgContour,[cnt],-1,(255,0,0),3)
img = cv2.imread("./a3.jpg")
imgContour = img.copy()
imgG = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgB = cv2.GaussianBlur(imgG,(7,7),1)
imgC = cv2.Canny(imgB,50,50)
getC(imgC)
cv2.imshow("",img)
cv2.imshow("g",imgG)
cv2.imshow("b",imgB)
cv2.imshow("l",imgContour)
cv2.waitKey(0)

how do I extract all pixel values from a certain ROI and then store it as a CSV file and again use that csv file to get back that ROI

After applying mask original image
import cv2
import dlib
import numpy as np
img = cv2.imread("Aayush.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
msk = np.zeros_like(img_gray)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
faces = detector(img_gray)
for face in faces:
landmarks = predictor(img_gray, face)
lp = []
for n in range(0,68):
x = landmarks.part(n).x
y = landmarks.part(n).y
lp.append((x,y))
p = np.array(lp, np.int32)
#cv2.circle(img, (x,y), 3, (0, 0, 255), -1)
convexhull = cv2.convexHull(p)
#cv2.polylines(img, [convexhull], True, (255,0,0), 3)
cv2.fillConvexPoly(msk, convexhull, 255)
img1 = cv2.bitwise_and(img, img, mask = msk)
img1 containsa complete black image with face cut from img, I just require the pixel values of face portion and not complete image
As original image and mask have not been provided in the question itself. I am assuming a simple input image and a mask image with circular cavity as:
The mask here is a single channel matrix with a value of 255 in the central cavity. To get the pixel info inside the cavity only you can use following numpy operation:
pixel_info = original_image[mask == 255]
# You may need to convert the numpy array to Python list.
pixel_info_list = pixel_info.tolist()
Now you may serialize the list to any format you want (csv in this case.)
Full code:
import cv2
import numpy as np
original_image = cv2.imread("/path/to/lena.png")
mask = np.zeros(original_image.shape[:2], dtype=original_image.dtype)
mask = cv2.circle(mask, (256, 256), 100, [255], -1)
pixel_info = original_image[mask == 255]
pixel_info_list = pixel_info.tolist()

How can i count segments in an image in python?

I am new to image processing and python. You might've seen my amateur codes on this site in the last couple of days.
I am trying to count the number of trees using aerial images. This is my code:
from PIL import Image
import cv2
import numpy as np
from skimage import io, filters, measure
from scipy import ndimage
img = Image.open("D:\\Texture analysis\\K-2.jpg")
row, col = img.size
hsvimg = img.convert('HSV')
hsvimg.mode = 'RGB'
hsvimg.save('newImage2.jpg')
npHSI = np.asarray(hsvimg) #Convert HSI Image to np image
blur = cv2.GaussianBlur(npHSI, (45, 45), 5)
assert isinstance(blur, np.ndarray) ##############################
assert len(blur.shape) == 3 #Convert np Image to HSI Image
assert blur.shape[2] == 3 ##############################
hsiBlur = Image.fromarray(blur, 'RGB')
hsiBlur.save('hsiBlur.jpg') #Save the blurred image
## Read
img = cv2.imread("D:\\Texture analysis\\hsiBlur.jpg")
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#Threshold the image and segment the trees
mask = cv2.inRange(hsv, (36, 25, 25), (70, 255,255))
imask = mask>0
green = np.zeros_like(img, np.uint8)
green[imask] = img[imask]
## save
cv2.imwrite("green.png", green)
#Count the number of trees
im = io.imread('green.png', as_grey=True)
val = filters.threshold_otsu(im)
drops = ndimage.binary_fill_holes(im < val)
labels = measure.label(drops)
print(labels.max())
Original image:
HSI image with gaussian filter:
Segmented image:
The last part of the code returns 7, which is a wrong output. The value should be above 50. How can I properly count the number of green segments in the final segmented image?
EDIT
I converted green.png to binary and applied erosion with a 3x3 filter and iterated it 7 times to remove the noise.
This is what I did at the end. I followed this stackoverflow link
##save
cv2.imwrite("green.png", green)
#Convert to grayscale
gray = np.dot(green[...,:3], [0.299, 0.587, 0.114])
cv2.imwrite("grayScale.jpg", gray)
#Binarize the grayscale image
ret,bin_img = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
cv2.imwrite("bin_img.jpg", bin_img)
#Erosion to remove the noise
kernel = np.ones((3, 3),np.uint8)
erosion = cv2.erode(gray, kernel, iterations = 7)
cv2.imwrite("erosion.jpg", erosion)
#Count the number of trees
finalImage = cv2.imread('erosion.jpg')
finalImage = cv2.cvtColor(finalImage, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(finalImage, 127, 255, 1)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
cv2.drawContours(finalImage,[cnt],0,(0,0,255),1)
Saurav mentioned in his answer ... size of "contours" will give you the count. This print(contour.size())gives an error and print(contour) just prints a long 2D array. How can i get the size of contour?
PS. I didn't upload the grayscale, binary and eroded image because i felt that the images were already taking too much space, I can still upload them if anyone wants to.
I've found 52 trees with that script:
from PIL import Image, ImageDraw, ImageFont
image = Image.open('04uX3.jpg')
pixels = image.load()
size = image.size
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('arial', 60)
i = 1
for x in range(0, size[0], 100):
for y in range(0, size[1], 100):
if pixels[x, y][1] > 200:
draw.text((x, y), str(i), (255, 0, 0), font=font)
i += 1
image.save('result.png')
You can see that some trees weren't detected and some non-trees were detected. So this is very rough calculation:

Image Cropping to get just the particular shape out of image

[I have the images as below, i need to extract just the white strip portion from all the images.
i Have tried using PIL to extract the rectangular portion by manually specifying the pixel value, Can there be any automated way to get this work done where by just feeding the image gives back the rectangular portion
Below is My snipped code:
from PIL import Image
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = Image.open('C:/Users/ShAgarwal/Documents/image_dataset/pic9.jpg')
half_the_width = img.size[0] / 2
half_the_height = img.size[1] / 2
img4 = img.crop(
(
half_the_width-1632,
half_the_height - 440,
half_the_width+1632,
half_the_height + 80
)
)
sample image
import cv2
import numpy as np
from matplotlib import pyplot as plt
image='IMG_3134.JPG'
# read image
imgc = cv2.imread(image)
img = cv2.resize(imgc, None, fx=0.25, fy=0.25) # resize since image is huge
#cropping the strip dimensions
#crop_img = img[1010:1650,140:1099723]
blurred = cv2.blur(img, (3,3))
canny = cv2.Canny(blurred, 50, 200)
Marking coordinates through auto image detection using canny's algorithm
## find the non-zero min-max coords of canny
pts = np.argwhere(canny>0)
y1,x1 = pts.min(axis=0)
y2,x2 = pts.max(axis=0)`
`## crop the region
cropped = img[y1:y2, x1:x2]
cv2.imwrite("cropped.png", cropped)
#Select the bounded area around white boundary
tagged = cv2.rectangle(img.copy(), (x1,y1), (x2,y2), (0,255,0), 3, cv2.LINE_AA)
r = cv2.selectROI(tagged)
imCrop = im[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
#Bounded Area
cv2.imwrite("taggd2.png", imcrop)
cv2.waitKey()
Results from above code

Resources