How to detect blurry blobs? - python

I would like to detect all the bright spots in this image (https://i.imgur.com/UnTWWHz.png)
The code I've tried is via thresholding, but it only detects the very bright ones. As you can see in the image below.
But some of the spots are out of focus which I need to also detect them.
Could you suggest a method? The picture below shows the blurred spots that I'd like to detect in yellow circles
I tried with the following code
import os
import cv2
import numpy as np
path="C:/Slides/Fluoroscent/E_03_G_O_subpics"
imgname="sub_2_4.png"
image = cv2.imread(os.path.join(path,imgname))
# constants
BINARY_THRESHOLD = 10
CONNECTIVITY = 4
DRAW_CIRCLE_RADIUS = 18
thr=50
# convert to gray
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# threshold the black/ non-black areas
_, thresh = cv2.threshold(gray_image, BINARY_THRESHOLD, thr, cv2.THRESH_BINARY)
# find connected components
components = cv2.connectedComponentsWithStats(thresh, CONNECTIVITY, cv2.CV_32S)
# draw circles around center of components
#see connectedComponentsWithStats function for attributes of components variable
centers = components[3]
for center in centers:
cv2.circle(image, (int(center[0]), int(center[1])), DRAW_CIRCLE_RADIUS, (0,0,255), thickness=1)
cv2.imwrite(os.path.join(path,"result_thresh_"+str(thr)+".png"), image)
cv2.imshow("result", image)
cv2.waitKey(0)

As mentioned in the comments you will get better results by changing the threshold values. I changed the values to 20 and 255 respectively and added erosion to get rid of some noise. You can play around with morphological transformations to get the exact desired result. Read more here .
Code:
import cv2
import numpy as np
kernel = np.ones((5,5),np.uint8)
CONNECTIVITY = 4
DRAW_CIRCLE_RADIUS = 18
img = cv2.imread('blobs.png')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray_img, 20, 255, cv2.THRESH_BINARY)
erosion = cv2.erode(thresh,kernel,iterations = 1)
components = cv2.connectedComponentsWithStats(erosion, CONNECTIVITY, cv2.CV_32S)
centers = components[3]
for center in centers:
cv2.circle(img, (int(center[0]), int(center[1])), DRAW_CIRCLE_RADIUS, (0,0,255), thickness=1)
cv2.imshow('Original', img)
cv2.imshow('Thresh', thresh)
cv2.imshow('Erosion', erosion)
cv2.waitKey(0)
Results:
Threshold
Erosion
Original with circles

Related

Detect scratches on the white Images

I have a data set that contains washing machine images with light and big deep scratches. I have tried the following two images with the the below-listed code.
Side cropped and rotated image:
Side cropped image but not rotated:
I have tried the following code from How to improve the detection of the defect on the surface? but it is not working for white images which show black screens in the output. I have a huge data set of such images but am not able to detect the scratches. How could I do this using the following code? If it's not possible then what would be the right approach?
import cv2
import numpy as np
# load image
img = cv2.imread('./detect-01.jpg')
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# adaptive threshold
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, -35)
# apply morphology
kernel = np.ones((3,30),np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((3,35),np.uint8)
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)
# get hough line segments
threshold = 25
minLineLength = 10
maxLineGap = 20
lines = cv2.HoughLinesP(morph, 1, 30*np.pi/360, threshold, minLineLength, maxLineGap)
# draw lines
linear1 = np.zeros_like(thresh)
linear2 = img.copy()
for [line] in lines:
x1 = line[0]
y1 = line[1]
x2 = line[2]
y2 = line[3]
cv2.line(linear1, (x1,y1), (x2,y2), 255, 1)
cv2.line(linear2, (x1,y1), (x2,y2), (0,0,255), 1)
print('number of lines:',len(lines))
# save resulting masked image
cv2.imwrite('scratches_thresh.jpg', thresh)
cv2.imwrite('scratches_morph.jpg', morph)
cv2.imwrite('scratches_lines1.jpg', linear1)
cv2.imwrite('scratches_lines2.jpg', linear2)
# display result
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("lines1", linear1)
cv2.imshow("lines2", linear2)
cv2.waitKey(0)
cv2.destroyAllWindows()
I will provide you few suggestions based on my experience.
Get the Canny of the gray scale image.
Canny thresholds for the lighter background images should be less. For example threshold1: 20, threshold2: 35 etc.
For dark background images canny thresholds can be 150, 220 etc.
Apply the houghline on the canny output image.
Try to use Houghlines first instead of HoughlinesP.
Draw the detected lines on image and adjust the threshold values.

Radius Determination for 2D data cloud

I have a 2D dataset and converted it to a point cloud using cv2.threshold. For this point cloud I want to determine the radius of the central circle. The approach with Hough Circles does not seem to work.
An example file from my dataset is here and my code for determining the point cloud is:
import numpy as np
import cv2
import copy
import matplotlib.pyplot as plt
import sys
image = cv2.imread("600.png")
orig_image = np.copy(image)
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#gray = cv2.medianBlur(gray, 1)
gray = cv2.GaussianBlur(gray, (1,1), 0)
# apply binary thresholding
ret, thresh = cv2.threshold(gray, 145, 255, cv2.THRESH_BINARY)
cv2.imshow('image', thresh)
cv2.waitKey(0)
cv2.imwrite('image_thres1.png', thresh)
cv2.destroyAllWindows()
contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
image_copy = image.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
cv2.imshow('None approximation', image_copy)
cv2.waitKey(0)
cv2.imwrite('contours_none_image1.png', image_copy)
cv2.destroyAllWindows()
For example my point cloud looks like this.
I then tried to estimate the radius by specifying what percentage of the data points should be in a circle, but this is not useful as small percentage changes change the radius extremely.
I can see the central circle without problems with my eye, but I can't find a way to determine the radius or just an enclosing circle.
Is there an elegant solution or algorithm for my problem?
After Gauss lowpass and binarization:
Note that your circle is not really a circle.

Removing White Text with Black Borders From Image

I am trying to remove text from images that has a black border with white fill. Take the image below as an example.
I have tried a few options utilizing opencv and skimage inpaint
import cv2
from skimage.restoration import inpaint
img = cv2.imread('Documents/test_image.png')
mask = cv2.threshold(img, 210, 255, cv2.THRESH_BINARY)[1][:,:,0]
dst = cv2.inpaint(img, mask, 7, cv2.INPAINT_TELEA)
image_result = inpaint.inpaint_biharmonic(img, mask,
multichannel=True)
cv2.imshow('image',img)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)
cv2.imshow('image_result',image_result)
cv2.waitKey(0)
It seems like the inpainting is just trying to fill with black as that is what it is identifying as being around the areas of interest. What I would like to do is remove the white text and black borders completely, or secondarily try to fill the white with more information from surrounding colors than just the black.
Here is the best solution I could come up with, still open to others with more experience showing me a better way if anyone has an idea.
mask = cv2.threshold(img, 245, 255, cv2.THRESH_BINARY)[1][:,:,0]
new_mask = cv2.dilate(mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10,10)))
dst = cv2.inpaint(img, new_mask, 7, cv2.INPAINT_TELEA)
Here are two inpainting methods in Python/OpenCV. Note that I use the saturation channel to create the threshold, since white and black have zero saturation, in principle.
Input:
import cv2
import numpy as np
# read input
img = cv2.imread('white_black_text.png')
# convert to hsv and extract saturation
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
sat = hsv[:,:,1]
# threshold and invert
thresh = cv2.threshold(sat, 10, 255, cv2.THRESH_BINARY)[1]
thresh = 255 - thresh
# apply morphology dilate
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_DILATE, kernel)
# do inpainting
result1 = cv2.inpaint(img,thresh,11,cv2.INPAINT_TELEA)
result2 = cv2.inpaint(img,thresh,11,cv2.INPAINT_NS)
# save results
cv2.imwrite('white_black_text_threshold.png', thresh)
cv2.imwrite('white_black_text_inpainted1.png', result1)
cv2.imwrite('white_black_text_inpainted2.png', result1)
# show results
cv2.imshow('thresh',thresh)
cv2.imshow('result1',result1)
cv2.imshow('result2',result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold and morphology cleaned result:
Result 1 (Telea):
Result 2 (Navier Stokes):

Find area with content and get its bouding rect

I'm using OpenCV 4 - python 3 - to find an specific area in a black & white image.
This area is not a 100% filled shape. It may hame some gaps between the white lines.
This is the base image from where I start processing:
This is the rectangle I expect - made with photoshop -:
Results I got with hough transform lines - not accurate -
So basically, I start from the first image and I expect to find what you see in the second one.
Any idea of how to get the rectangle of the second image?
I'd like to present an approach which might be computationally less expensive than the solution in fmw42's answer only using NumPy's nonzero function. Basically, all non-zero indices for both axes are found, and then the minima and maxima are obtained. Since we have binary images here, this approach works pretty well.
Let's have a look at the following code:
import cv2
import numpy as np
# Read image as grayscale; threshold to get rid of artifacts
_, img = cv2.threshold(cv2.imread('images/LXSsV.png', cv2.IMREAD_GRAYSCALE), 0, 255, cv2.THRESH_BINARY)
# Get indices of all non-zero elements
nz = np.nonzero(img)
# Find minimum and maximum x and y indices
y_min = np.min(nz[0])
y_max = np.max(nz[0])
x_min = np.min(nz[1])
x_max = np.max(nz[1])
# Create some output
output = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.rectangle(output, (x_min, y_min), (x_max, y_max), (0, 0, 255), 2)
# Show results
cv2.imshow('img', img)
cv2.imshow('output', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
I borrowed the cropped image from fmw42's answer as input, and my output should be the same (or most similar):
Hope that (also) helps!
In Python/OpenCV, you can use morphology to connect all the white parts of your image and then get the outer contour. Note I have modified your image to remove the parts at the top and bottom from your screen snap.
import cv2
import numpy as np
# read image as grayscale
img = cv2.imread('blackbox.png')
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
_,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)
# apply close to connect the white areas
kernel = np.ones((75,75), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# get contours (presumably just one around the outside)
result = img.copy()
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for cntr in contours:
x,y,w,h = cv2.boundingRect(cntr)
cv2.rectangle(result, (x, y), (x+w, y+h), (0, 0, 255), 2)
# show thresh and result
cv2.imshow("thresh", thresh)
cv2.imshow("Bounding Box", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save resulting images
cv2.imwrite('blackbox_thresh.png',thresh)
cv2.imwrite('blackbox_result.png',result)
Input:
Image after morphology:
Result:
Here's a slight modification to #fmw42's answer. The idea is connect the desired regions into a single contour is very similar however you can find the bounding rectangle directly since there's only one object. Using the same cropped input image, here's the result.
We can optionally extract the ROI too
import cv2
# Grayscale, threshold, and dilate
image = cv2.imread('3.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Connect into a single contour and find rect
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=1)
x,y,w,h = cv2.boundingRect(dilate)
ROI = original[y:y+h,x:x+w]
cv2.rectangle(image, (x, y), (x+w, y+h), (36, 255, 12), 2)
cv2.imshow('image', image)
cv2.imshow('ROI', ROI)
cv2.waitKey()

Contours from a image appear very sloppy with cv2 findContours. How to improve?

I'm trying to find the contour of an image using cv2. There are many related questions, but the answer always appear to be very specific and not applicable to my case.
I have an black and white image that I change into color.
thresh = cv2.cvtColor(thresh, cv2.COLOR_RGB2GRAY)
plt.imshow(thresh)
Next, I try to find the contours.
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
and then I visualize it by plotting it on a black background.
blank_image = np.zeros((thresh.shape[0],thresh.shape[1],3), np.uint8)
img = cv2.drawContours(blank_image, contours, 0, (255,255,255), 3)
plt.imshow(img)
The contour follows the actual contour, i.e. surrounding the whole thing. How do I get something like this very bad paint impression:
You can use Canny edge detection to do this:
import cv2
frame = cv2.imread("iCyrOT3.png") # read a frame
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # turn it gray
edges = cv2.Canny(gray, 100, 200) # get canny edges
cv2.imshow('Test', edges) # display the result
cv2.waitKey(0)

Categories

Resources