I have samples images of stones present in the images. I need to identify the visible stones only. The approach which I tried is threshold based filtering and detecting cv2.contours. Also, I am looking into ENet Architecture for semantic segmentation based deep learning approach. The samples images are below.
Example image1:
Example image2:
The code which I tried for contour based detection is as below
image = cv2.imread(os.path.join(img_path, img_name2))
# threshold based customization
lower_bound = np.array([0, 0, 0])
upper_bound = np.array([250,55,100])
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
#masking the image using inRange() function
imagemask = cv2.inRange(hsv, lower_bound, upper_bound)
plt.figure(figsize=(20,10))
plt.imshow(imagemask, cmap="gray")
# erode and diluation to smoothen the edeges
final_mask = cv2.erode(imagemask, np.ones((3, 3), dtype=np.uint8))
final_mask = cv2.dilate(imagemask, np.ones((5, 5), dtype=np.uint8))
# find contours based on the mask
contours = cv2.findContours(final_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# draw contours
img_conts = cv2.drawContours(image.copy(), contours[0], -1, (0,255,0), 3)
plt.figure(figsize=(20,10))
plt.imshow(img_conts, cmap="gray")
The sample contours ouput. I know that the thresholds can be tuned for better results here.
But, what I am looking here for the any better approach or solution can work in this heavy environment for detection small particles like stones. Any ideas to solve in better way?
Here is how you can use the Canny edge detector to detect the rocks in your images:
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 103, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (23, 23), 0)
img_canny = cv2.Canny(img_blur, 65, 0)
img_dilate = cv2.dilate(img_canny, None, iterations=2)
return cv2.erode(img_dilate, None, iterations=2)
imgs = [cv2.imread("image1.jpg"), cv2.imread("image2.jpg")]
for i, img in enumerate(imgs):
contours = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
cv2.drawContours(img, contours, -1, (0, 255, 0), 1)
cv2.imshow(str(i), img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output for sample images 1 and 2:
You can also tweak the parameters using OpenCV trackbars using the code below:
import cv2
import numpy as np
from random import randint, sample
def process(img, c_t1, c_t2):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 103, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (23, 23), 0)
img_canny = cv2.Canny(img_blur, c_t1, c_t2)
img_dilate = cv2.dilate(img_canny, None, iterations=2)
return cv2.erode(img_dilate, None, iterations=2)
def show(imgs, win="Image", scale=1):
imgs = [cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) if len(img.shape) == 2 else img for img in imgs]
img_concat = np.concatenate(imgs, 1)
h, w = img_concat.shape[:2]
cv2.imshow(win, cv2.resize(img_concat, (int(w * scale), int(h * scale))))
d = {"Canny Threshold 1": (65, 500),
"Canny Threshold 2": (0, 500)}
imgs = [cv2.imread("image1.jpg"), cv2.imread("image2.jpg")]
cv2.namedWindow("Track Bars")
for i in d:
cv2.createTrackbar(i, "Track Bars", *d[i], id)
while True:
c_t1, c_t2 = (cv2.getTrackbarPos(i, "Track Bars") for i in d)
for i, img in enumerate(imgs):
img_copy = img.copy()
processed = process(img, c_t1, c_t2)
contours = cv2.findContours(processed, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
cv2.drawContours(img_copy, contours, -1, (0, 255, 0), 1)
show([img_copy, processed], str(i))
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
Output:
(Click image to expand)
Related
Just used the following code a OCR application. The OCR to be read is on a metal milled surface with a unique font. The below code work well for embossed surfaces but not engraved surfaces. I have tried tweaking the blur and dilate iterations, still no results.
How i can add new fonts into tesseract , if the unique font is an issue ?
Any tips on how i can get better results ?
import cv2
import numpy as np
import imutils
import pytesseract
# read image from disk
image = cv2.imread('test.jpg')
# make it gray
img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# blur it to remove noise
img = cv2.GaussianBlur(img, (7,7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(img, 40, 90)
dilate = cv2.dilate(edged, None, iterations=1)
# perform erosion if necessay, it completely depends on the image
#erode = cv2.erode(dilate, None, iterations=1)
# create an empty masks
mask = np.ones(img.shape[:2], dtype="uint8") * 255
# find contours
cnts = cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[1] if imutils.is_cv2() else cnts[0]
orig = img.copy()
for c in cnts:
# if the contour is not sufficiently large, ignore it
if cv2.contourArea(c) < 300:
cv2.drawContours(mask, [c], -1, 0, -1)
x,y,w,h = cv2.boundingRect(c)
# filter more contours if nessesary
if(w>h):
cv2.drawContours(mask, [c], -1, 0, -1)
newimage = cv2.bitwise_and(dilate.copy(), dilate.copy(), mask=mask)
img2 = cv2.dilate(newimage, None, iterations=5)
ret2,th1 = cv2.threshold(img2 ,0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
pytesseract.pytesseract.tesseract_cmd = r'root\folder'
# Tesseract OCR on the image
temp = pytesseract.image_to_string(th1)
# Write results on the image
cv2.putText(image, temp, (100,100), cv2.FONT_HERSHEY_SIMPLEX, 1.8, (0,255,255), 3)
# show the outputs
cv2.imshow('Original image', cv2.resize(image,(640,480)))
cv2.imshow('Dilated', cv2.resize(dilate,(640,480)))
cv2.imshow('New Image', cv2.resize(newimage,(640,480)))
cv2.imshow('Inverted Threshold', cv2.resize(th1,(640,480)))
cv2.waitKey(0)
cv2.destroyAllWindows()
I'm trying use OpenCV (4.4.0) to draw the contours of digits in image. But it doesn't work for me. Below my code:
# Read the input image and deskew it
im = cv2.imread('1742.jpg')
# Convert to grayscale and apply Gaussian filtering to filter noisy pixels
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_gray = cv2.GaussianBlur(im_gray, (5, 5), 0)
# Threshold the image
ret, im_th = cv2.threshold(im_gray, 127, 255, cv2.THRESH_BINARY_INV)
# Find contours in the image
contours, hierarchy = cv2.findContours(im_th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(im_th, contours, -1, (0,255,0), 3)
cv2.imshow("Contours", im_th)
Here is the image and the output of my code. I expect it has a green contour per digit.
1742.jpg
import cv2
im = cv2.imread('1742.jpg')
blur = cv2.GaussianBlur(im, (5, 5), 0)
edged = cv2.Canny(blur, 0, 150)
contours, _ = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
"""
draw a rectangle around those contours on main image
"""
[x,y,w,h] = cv2.boundingRect(contour)
cv2.rectangle(im, (x,y), (x+w,y+h), (0, 255, 0), 1)
cv2.imshow('Final Image with Contours', im)
cv2.waitKey()
cv2.imwrite('final.jpg',im)
I used Canny edge detection prior to the findContours method.Here is the result
If you want to draw the contours on the digits then use
import cv2
im = cv2.imread('1742.jpg')
blur = cv2.GaussianBlur(im, (5, 5), 0)
edged = cv2.Canny(blur, 0, 150)
contours, _ = cv2.findContours(edged, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(im, contours, -1, (0,255,0), 3)
cv2.imshow('Final Image with Contours', im)
cv2.waitKey()
Then the result is this
I'm trying to detect the contour of this image in order to crop it in openCV.
I've come up with working code, however, if there is some slight background on the image, it will fail.
Image processing:
Detect boundaries (blue dots):
Crop/rotate:
However, with an image like this, with some background light, it wouldn't work:
preprocess:
Boundaries detection:
def preProcessing(img):
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
adaptive_thresold1 = 31
adaptive_thresold2 = 7
blur = cv2.blur(imgGray, (3, 3))
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,adaptive_thresold1,adaptive_thresold2)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
stackedImages = hp.stackImages(0.1,([img,thresh, close],[img,thresh, close]))
cv2.imshow("WorkFlow", stackedImages)
cv2.waitKey(0)
return thresh
def getContours(img):
biggest = np.array([])
maxArea = 0
img = cv2.bitwise_not(img)
contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area>5000:
print (area)
#cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3)
peri = cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,0.02*peri,True)
if area >maxArea and len(approx) == 4:
biggest = approx
maxArea = area
print ("ok")
print (biggest)
out = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.drawContours(out, biggest, -1, (255, 0, 0), 50)
stackedImages = hp.stackImages(0.1,([img,out],[img,out]))
cv2.imshow("WorkFlow", stackedImages)
cv2.waitKey(0)
return biggest
Any suggestion to make this code more reliable ?
Instead of using adaptive thresholding, try using Otsu's thresholding.
Change this line
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,adaptive_thresold1,adaptive_thresold2)
in your code to -
retval_blue, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
This worked for me in the image.
I want to extract some rectangles at the top from a UML sequence diagram in jpg format by using OpenCV.
The algorithm I use finds way too many rectangles that are super small and not needed.
I think the mess up is somewhere in the beginning of the code where I apply canny edge detection but I am not sure.
I want to capture only the big rectangles from the top and center.
Thanks for any help.
import cv2
import numpy as np
import imutils
image = cv2.imread("./diagrams/sd2.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 90, 150, 3)
cnts = cv2.findContours(edges, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cv2.drawContours(image, cnts, -1, (0, 255, 0), 1)
def detect(c):
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.03 * peri, True)
if len(approx) == 4:
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
return shape
# loop over the contours
for c in cnts:
M = cv2.moments(c)
if M["m00"] != 0:
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
shape = detect(c)
c = c.astype("float")
c = c.astype("int")
if(shape == "rectangle"):
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (0, 0, 0), 2)
# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)
I am trying to find the contours of an animal from a picture. Let's assume it is a chicken. From the picture I could find its contours but they aren't closed. Also, I am getting a lot of noise from the background which is white ( same as the chicken).
I am using a simple code found on stackoverflow.
import numpy as np
import cv2
img = cv2.imread('lateral.jpg')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# blurred = cv2.GaussianBlur(imgray, (5, 5), 0)
# edged = cv2.Canny(blurred, 10, 11) # 10 and 40 to be more perceptive
# contours_canny= cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]
edges = cv2.Canny(imgray, 10,30)
cv2.imshow('edges', edges)
k = cv2.waitKey()
Is there a way to find just the contour of this chicken?
Thanks in advance.
Finding contour is quite easy. The problem is that your image has low contrast between the chicken and the background. So, your idea of using canny edges was not bad, it just needed some post processing.
I guess this is what you are looking for:
import cv2
import numpy as np
image = cv2.imread("./chicken.jpg", cv2.IMREAD_COLOR)
image = cv2.resize(image, (0,0), fx=0.5, fy=0.5)
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)[...,0]
edges = cv2.Canny(imgray, 10,30)
blurred = cv2.GaussianBlur(edges, (9, 9), 0)
clahe = cv2.createCLAHE(clipLimit=5.0, tileGridSize=(32,32))
contrast = clahe.apply(blurred)
ret, thresh = cv2.threshold(contrast, 20, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
_, contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
maxArea = 0
best = None
for contour in contours:
area = cv2.contourArea(contour)
print (area)
if area > maxArea :
maxArea = area
best = contour
cv2.drawContours(image, [best], 0, (0, 0, 255), -1)
while True:
cv2.imshow("result", image)
k = cv2.waitKey(30) & 0xff
if k == 27:
break