Opencv unable to get ROI of highest intensity part - python

I have tried this code.
import sys
import numpy as np
sys.path.append('/usr/local/lib/python2.7/site-packages')
import cv2
from cv2.cv import *
img=cv2.imread("test2.jpg",cv2.IMREAD_COLOR)
gimg = cv2.imread("test2.jpg",cv2.IMREAD_GRAYSCALE)
b,g,r = cv2.split(img)
ret,thresh1 = cv2.threshold(gimg,127,255,cv2.THRESH_BINARY);
numrows = len(thresh1)
numcols = len(thresh1[0])
thresh = 170
im_bw = cv2.threshold(gimg, thresh, 255, cv2.THRESH_BINARY)[1]
trig=0
trigmax=0;
xmax=0
ymax=0
for x in range(0,numrows):
for y in range(0,numcols):
if(im_bw[x][y]==1):
trig=trig+1;
if(trig>5):
xmax=x;
ymax=y;
break;
print x,y,numrows,numcols,trig
roi=gimg[xmax:xmax+200,ymax-500:ymax]
cv2.imshow("roi",roi)
WaitKey(0)
here test2.jpg is what I am tring to do is to concentrate on the high intensity part of the image(i.e the circle with high intensity in image).But my code does not seem to do so.
Can anyone help?

I found answer to my question from here
here is my code
# import the necessary packages
import numpy as np
import cv2
# load the image and convert it to grayscale
image = cv2.imread('test2.jpg')
orig = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# apply a Gaussian blur to the image then find the brightest
# region
gray = cv2.GaussianBlur(gray, (41, 41), 0)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
image = orig.copy()
roi=image[maxLoc[1]-250:maxLoc[1]+250,maxLoc[0]-250:maxLoc[0]+250,2:2]
cv2.imshow("Robust", roi)
cv2.waitKey(0)
test2.jpg
ROI

Try checking whether a pixel is not zero - it turns out those pixels have a value of 255 after thresholding, as it is a grayscale image after all.
The threshold seems to be wrong also, but I don't really know what you want to see (display it with imshow - it isn't just the circle). And your code matches the number '3' in the bottom left corner, therefore the ROI matrix indices are invalid in your example.
EDIT:
After playing around with the image, I ended up using a different approach. I used the SimpleBlobDetector and did an erosion on the image before, so the region you're interested in remains connected. For the blob detector the program inverts the image first. (You may want to read a SimpleBlobDetector tutorial as I did, parts of the code are based on that page - many thanks to the author!)
The following code displays the procedure step by step:
import cv2
import numpy as np
# Read image
gimg = cv2.imread("test2.jpg", cv2.IMREAD_GRAYSCALE)
# Invert the image
im_inv = 255 - gimg
cv2.imshow("Step 1 - inverted image", im_inv)
cv2.waitKey(0)
# display at a threshold level of 50
thresh = 45
im_bw = cv2.threshold(im_inv, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imshow("Step 2 - bw threshold", im_bw)
cv2.waitKey(0)
# erosion
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(10,10))
im_bw = cv2.erode(im_bw, kernel, iterations = 1)
cv2.imshow('Step 3 - erosion connects disconnected parts', im_bw)
cv2.waitKey(0)
# Set up the detector with default parameters.
params = cv2.SimpleBlobDetector_Params()
params.filterByInertia = False
params.filterByConvexity = False
params.filterByCircularity = False
params.filterByColor = False
params.minThreshold = 0
params.maxThreshold = 50
params.filterByArea = True
params.minArea = 1000 # you may check with 10 --> finds number '3' also
params.maxArea = 100000 #im_bw.shape[0] * im_bw.shape[1] # max limit: image size
# Create a detector with the parameters
ver = (cv2.__version__).split('.')
if int(ver[0]) < 3 :
detector = cv2.SimpleBlobDetector(params)
else :
detector = cv2.SimpleBlobDetector_create(params)
# Detect blobs.
keypoints = detector.detect(im_bw)
print "Found", len(keypoints), "blobs:"
for kpt in keypoints:
print "(%.1f, %.1f) diameter: %.1f" % (kpt.pt[0], kpt.pt[1], kpt.size)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the
# circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(gimg, keypoints, np.array([]), (0,0,255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
This algorithm finds the coordinate (454, 377) as the center of the blob, but if you reduce the minArea to e.g. 10 then it will find the number 3 in the bottom corner as well.

Related

OpenCV python overlapping particles size and number

I got greyscale images which show particles on a surface. I like to write a program which finds the particles draws a circle around and gives counts the circles and the pixels inside the circles.
One of the main problems is that the particles overlapp. The next problem is that the contrast of the images is changing, from one image to the next.
Here is my first trial:
import matplotlib.pyplot as plt
import cv2 as cv
import imutils
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import os.path
fileref="test.png"
original = cv.imread(fileref)
img = original
cv.imwrite( os.path.join("inverse_"+fileref[:-4]+".png"), ~img );
img = cv.medianBlur(img,5)
img_grey = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret,th1 = cv.threshold(img_grey,130,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img_grey,255,cv.ADAPTIVE_THRESH_MEAN_C,\
cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img_grey,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1]
cv.imwrite( os.path.join("threshhold_"+fileref[:-4]+".jpg"), th1 );
cv.imwrite( os.path.join("adaptivthreshhold-m_"+fileref[:-4]+".jpg"), th2 );
cv.imwrite( os.path.join("adaptivthreshhold-g_"+fileref[:-4]+".jpg"), th3 );
imghsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
imghsv[:,:,2] = [[max(pixel - 25, 0) if pixel < 190 else min(pixel + 25, 255) for pixel in row] for row in imghsv[:,:,2]]
cv.imshow('contrast', cv.cvtColor(imghsv, cv.COLOR_HSV2BGR))
# Setup SimpleBlobDetector parameters.
params = cv.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 0
params.maxThreshold = 150
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.87
# Filter by Inertia
params.filterByInertia = True
params.minInertiaRatio = 0.08 # 0.08
# Set edge gradient
params.thresholdStep = 0.5
# Filter by Area.
params.filterByArea = True
params.minArea = 300
# Set up the detector with default parameters.
detector = cv.SimpleBlobDetector_create(params)
# Detect blobs.
keypoints = detector.detect(original)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv.drawKeypoints(original, keypoints, np.array([]), (0, 0, 255),
cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
print(len(keypoints))
# Show keypoints
display=cv.resize(im_with_keypoints,None,fx=0.5,fy=0.5)
cv.imshow("Keypoints", display)
cv.waitKey(0)
cv.imwrite( os.path.join("keypoints_"+fileref[:-4]+".jpg"), im_with_keypoints );
It circles most particles but the parameters need to be changed for each image to get better results the circles can't overlapp and I don't know how to count the circles or count the pixels inside the circles.
Any help or hints which point me in the right direction are much appreciated.
I added a couple sample pics
This is an alternative approach and may not necessarily give better results than what you already have. You can try out plugging in different values for parameters and see if it gives you acceptable results.
import numpy as np
import cv2
import matplotlib.pyplot as plt
rgb = cv2.imread('/your/image/path/blobs_0002.jpeg')
gray = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
imh, imw = gray.shape
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY_INV,21,2)
th = cv2.adaptiveThreshold(gray,255, cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY_INV,15,15)
contours, hier = cv2.findContours(th.copy(),cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
out_img = rgb.copy()
for i in range(len(contours)):
if hier[0][i][3] != -1:
continue
x,y,w,h = cv2.boundingRect(contours[i])
ar = min(w,h)/max(w,h)
area = cv2.contourArea(contours[i])
extent = area / (w*h)
if 20 < w*h < 1000 and \
ar > 0.5 and extent > 0.4:
cv2.circle(out_img, (int(x+w/2), int(y+h/2)), int(max(w,h)/2), (255, 0, 0), 1)
plt.imshow(out_img)
For larger coalesced blobs you might try running Hough circles to see if partial contours fit the test. Just a thought. Just to acknowledge the fact that the images you are dealing with are challenging to come up with a clean solution.

OpenCVs BlobDetector crashes

I'm trying to use OpenCV's SimpleBlobDetector. I'm using a very siple code, I read the image, I create the detector and I apply it. But when I excecute it I get the error Python has stopped working.
The image is properly read
Is a 1536x2048 image (too big?)
Could the PGN format be a problem?
import cv2
import numpy as np;
# Read image
im = cv2.imread("vlcsnap-2020-02-05-10h54m17s200.png", cv2.IMREAD_GRAYSCALE)
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector()
# Detect blobs.
keypoints = detector.detect(im) #No error if I coment this line
Does anyone have a clue of why would't this work?
Here is an answer that I posted some time ago in Python/OpenCV for doing blob analysis. Perhaps this will help you.
Input:
import numpy as np
import cv2
import math
# read image
img = cv2.imread("particles.jpg")
# convert to grayscale
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# apply Gaussian Blur
smoothed = cv2.GaussianBlur(gray, (0,0), sigmaX=9, sigmaY=9, borderType = cv2.BORDER_DEFAULT)
# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(smoothed, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 65, 10)
cv2.imshow("Threshold", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Set up the SimpleBlobdetector with default parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 0
params.maxThreshold = 256
# Filter by Area.
params.filterByArea = True
params.minArea = 30
params.maxArea = 10000
# Filter by Color (black=0)
params.filterByColor = True
params.blobColor = 0
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.5
params.maxCircularity = 1
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.5
params.maxConvexity = 1
# Filter by InertiaRatio
params.filterByInertia = True
params.minInertiaRatio = 0
params.maxInertiaRatio = 1
# Distance Between Blobs
params.minDistBetweenBlobs = 0
# Do detecting
detector = cv2.SimpleBlobDetector_create(params)
# Get keypoints
keypoints = detector.detect(thresh)
print(len(keypoints))
print('')
# Get keypoint locations and radius
for keypoint in keypoints:
x = int(keypoint.pt[0])
y = int(keypoint.pt[1])
s = keypoint.size
r = int(math.floor(s/2))
print (x,y,r)
#cv2.circle(img, (x, y), r, (0, 0, 255), 2)
# Draw blobs
blobs = cv2.drawKeypoints(thresh, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow("Keypoints", blobs)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Save result
cv2.imwrite("particle_blobs.jpg", blobs)

How to detect blurry blobs?

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

Feather cropped edges

I'm trying to crop an object from an image, and paste it on another image. Examining the method in this answer, I've successfully managed to do that. For example:
The code (show_mask_applied.py):
import sys
from pathlib import Path
from helpers_cv2 import *
import cv2
import numpy
img_path = Path(sys.argv[1])
img = cmyk_to_bgr(str(img_path))
threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)
mask = mask_from_contours(img, contours)
mask = dilate_mask(mask, 50)
crop = cv2.bitwise_or(img, img, mask=mask)
bg = cv2.imread("bg.jpg")
bg_mask = cv2.bitwise_not(mask)
bg_crop = cv2.bitwise_or(bg, bg, mask=bg_mask)
final = cv2.bitwise_or(crop, bg_crop)
cv2.imshow("debug", final)
cv2.waitKey(0)
cv2.destroyAllWindows()
helpers_cv2.py:
from pathlib import Path
import cv2
import numpy
from PIL import Image
from PIL import ImageCms
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
def cmyk_to_bgr(cmyk_img):
img = Image.open(cmyk_img)
if img.mode == "CMYK":
img = ImageCms.profileToProfile(img, "Color Profiles\\USWebCoatedSWOP.icc", "Color Profiles\\sRGB_Color_Space_Profile.icm", outputMode="RGB")
return cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)
def threshold(img, thresh=128, maxval=255, type=cv2.THRESH_BINARY):
if len(img.shape) == 3:
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
threshed = cv2.threshold(img, thresh, maxval, type)[1]
return threshed
def find_contours(img):
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
contours = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return contours[-2]
def mask_from_contours(ref_img, contours):
mask = numpy.zeros(ref_img.shape, numpy.uint8)
mask = cv2.drawContours(mask, contours, -1, (255,255,255), -1)
return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
def dilate_mask(mask, kernel_size=11):
kernel = numpy.ones((kernel_size,kernel_size), numpy.uint8)
dilated = cv2.dilate(mask, kernel, iterations=1)
return dilated
Now, instead of sharp edges, I want to crop with feathered/smooth edges. For example (the right one; created in Photoshop):
How can I do that?
All images and codes can be found that at this repository.
You are using a mask to select parts of the overlay image. The mask currently looks like this:
Let's first add a Gaussian blur to this mask.
mask_blurred = cv2.GaussianBlur(mask,(99,99),0)
We get to this:
Now, the remaining task it to blend the images using the alpha value in the mask, rather than using it as a logical operator like you do currently.
mask_blurred_3chan = cv2.cvtColor(mask_blurred, cv2.COLOR_GRAY2BGR).astype('float') / 255.
img = img.astype('float') / 255.
bg = bg.astype('float') / 255.
out = bg * (1 - mask_blurred_3chan) + img * mask_blurred_3chan
The above snippet is quite simple. First, transform the mask into a 3 channel image (since we want to mask all the channels). Then transform the images to float, since the masking is done in floating point. The last line does the actual work: for each pixel, blends the bg and img images according to the value in the mask. The result looks like this:
The amount of feathering is controlled by the size of the kernel in the Gaussian blur. Note that it has to be an odd number.
After this, out (the final image) is still in floating point. It can be converted back to int using:
out = (out * 255).astype('uint8')
While Paul92's answer is more than enough, I wanted to post my code anyway for any future visitor.
I'm doing this cropping to get rid of white background in some product photos. So, the main goal is to get rid of the whites while keeping the product intact. Most of the product photos have shadows on the ground. They are either the ground itself (faded), or the product's shadow, or both.
While the object detection works fine, these shadows also count as part of the object. Differentiating the shadows from the objects is not really necessary, but it results in some images that are not so desired. For example, examine the left and bottom sides of the image (shadow). The cut/crop is obviously visible, and doesn't look all that nice.
To get around this problem, I wanted to do non-rectangular crops. Using masks seems to do the job just fine. The next problem was to do the cropping with feathered/blurred edges so that I can get rid of these visible shadow cuts. With the help of Paul92, I've managed to do that. Example output (notice the missing shadow cuts, the edges are softer):
Operations on the image(s):
The code (show_mask_feathered.py, helpers_cv2.py)
import sys
from pathlib import Path
import cv2
import numpy
from helpers_cv2 import *
img_path = Path(sys.argv[1])
img = cmyk_to_bgr(str(img_path))
threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)
dilation_length = 51
blur_length = 51
mask = mask_from_contours(img, contours)
mask_dilated = dilate_mask(mask, dilation_length)
mask_smooth = smooth_mask(mask_dilated, odd(dilation_length * 1.5))
mask_blurred = cv2.GaussianBlur(mask_smooth, (blur_length, blur_length), 0)
mask_blurred = cv2.cvtColor(mask_blurred, cv2.COLOR_GRAY2BGR)
mask_threshed = threshold(mask_blurred, 1)
mask_contours = find_contours(mask_threshed)
mask_contour = max_contour(mask_contours)
x, y, w, h = cv2.boundingRect(mask_contour)
img_cropped = img[y:y+h, x:x+w]
mask_cropped = mask_blurred[y:y+h, x:x+w]
background = numpy.full(img_cropped.shape, (200,240,200), dtype=numpy.uint8)
output = alpha_blend(background, img_cropped, mask_cropped)

minimum blob size for opencv threshold in python

is there a way to threshold only if the blob size is more than (height,width)?
Example
import cv2
img_1 = cv2.imread('my_image_1.jpg')
thresh = cv2.threshold(img_1, 200, 255, cv2.THRESH_BINARY)[1]
For the purposes of thresholding, I want to ignore all pixels that are not inside a blob of say, 6 x 6 pixels that also meet the thresholding condition.
What is the best way to do this?
Please check this tutorial.
You can implement this by adding the following lines to your code,
params = cv2.SimpleBlobDetector_Params()
params.filterByArea = True
params.minArea = 20 #define minimum area
ver = (cv2.__version__).split('.')
if int(ver[0]) < 3 :
detector = cv2.SimpleBlobDetector(params)
else :
detector = cv2.SimpleBlobDetector_create(params)
keypoints = detector.detect(thresh)
im_with_keypoints = cv2.drawKeypoints(thresh, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

Categories

Resources