Not enough background filtering - python

I am trying to filter the background of images presenting electric cables. I tried to do the following:
Transform from color to gray
Apply cv2.Laplacian or 2 times of cv2.Sobel for finding edges in both directions.
Apply thresholding cv2.THRESH_BINARY(_INV), cv2.THRESH_OTSU
Lastly, I tried to find edges with 'filtered' images using cv2.Canny together with cv2.HoughLinesP
Overall, the results aren't satisfying at all. I will give an example of 2 images:
And the output of my script:
I also played with the values in config, but the results weren't different much.
Here's the little script I managed to do:
import cv2
import matplotlib.pyplot as plt
import numpy as np
def img_show(images, cmap=None):
fig = plt.figure(figsize=(17, 10))
root = 3 # len(images) ** 0.5
for i, img in enumerate(images):
ax = fig.add_subplot(root, root, i + 1)
ax.imshow(img, cmap=cmap[i])
plt.show()
class Config:
scale = 0.4
min_threshold = 120
max_threshold = 200
canny_min_threshold = 100
canny_max_threshold = 200
config = Config()
def find_lines(img, rgb_img):
dst = cv2.Canny(img, config.canny_min_threshold, config.canny_max_threshold)
cdstP = np.copy(rgb_img)
lines = cv2.HoughLinesP(dst, 1, np.pi / 180, 150, None, 0, 0)
lines1 = lines[:, 0, :]
for x1, y1, x2, y2 in lines1[:]:
cv2.line(cdstP, (x1, y1), (x2, y2), (255, 0, 0), 5)
return cdstP
if __name__ == "__main__":
bgr_img = cv2.imread('DJI_0009.JPG')
bgr_img = cv2.resize(bgr_img, (0, 0), bgr_img, config.scale, config.scale)
rgb_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB)
gray_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2GRAY)
# _, threshold = cv2.threshold(gray_img, config.min_threshold, config.max_threshold, cv2.THRESH_BINARY)
# laplacian = cv2.Laplacian(rgb_img, cv2.CV_8UC1)
sobelx = cv2.Sobel(gray_img, cv2.CV_8UC1, 1, 0)
sobely = cv2.Sobel(gray_img, cv2.CV_8UC1, 0, 1)
blended = cv2.addWeighted(src1=sobelx, alpha=0.5, src2=sobely, beta=0.5, gamma=0)
_, threshold = cv2.threshold(blended, config.min_threshold, config.max_threshold,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)
p1 = find_lines(threshold, rgb_img)
p2 = find_lines(blended, rgb_img)
p3 = find_lines(gray_img, rgb_img)
plots = [rgb_img, p1, p2, p3]
cmaps = [None] + ['gray'] * (len(plots) - 1)
img_show(plots, cmaps)
I am assuming I need to do much better filtring. However, I also tried image segmentation, but the results weren't promising at all.
Any ideas on how to improve this?
Thanks

Here is one way to do that in Python/OpenCV. I threshold, then optionally clean with morphology. Then get the contours and for each contour compute its rotated rectangle. Then get the dimensions of the rotated rectangle and compute the aspect ratio (largest dimension / smallest dimension) and optionally the area. Then I threshold on the aspect ratio (and optionally the area) and keep only those contours that pass)
Input:
import cv2
import numpy as np
image = cv2.imread("DCIM-100-MEDIA-DJI-0009-JPG.jpg")
hh, ww = image.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# create a binary thresholded image
thresh = cv2.threshold(gray, 64, 255, cv2.THRESH_BINARY)[1]
# invert so line is white on black background
thresh = 255 - thresh
# apply morphology
kernel = np.ones((11,11), np.uint8)
clean = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# get external contours
contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
area_thresh = ww / 2
aspect_thresh = ww / 30
print(area_thresh,aspect_thresh)
print('')
result = image.copy()
for c in contours:
# get rotated rectangle from contour
# get its dimensions
rotrect = cv2.minAreaRect(c)
(center), (dim1,dim2), angle = rotrect
maxdim = max(dim1,dim2)
mindim = min(dim1,dim2)
area = dim1 * dim2
if mindim != 0:
aspect = maxdim / mindim
#print(area, aspect)
#if area > area_thresh and aspect > aspect_thresh:
if aspect > aspect_thresh:
# draw contour on input
cv2.drawContours(result,[c],0,(0,0,255),3)
print(area, aspect)
# save result
cv2.imwrite("DCIM-100-MEDIA-DJI-0009-JPG_thresh.jpg",thresh)
cv2.imwrite("DCIM-100-MEDIA-DJI-0009-JPG_clean.jpg",clean)
cv2.imwrite("DCIM-100-MEDIA-DJI-0009-JPG_result.jpg",result)
# display result
cv2.imshow("thresh", thresh)
cv2.imshow("clean", clean)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Thresholded image:
Morphology cleaned image:
Result image:

Related

I have using opencv python form converting the analog clock to a digital data for the hours and minutes but I need it to show for seconds too

I have ued opencv to read the image , convert it to gray scale, and found edges using canny, kernel, thesh, erode and so on and i have detected all the lines in the image using HooughLineP() and i have detected the hours and minutes hand but i also need to find the seconds hand here is the code which i have used
import cv2
import math
import numpy as np
from matplotlib import pyplot as plt
from math import sqrt
from math import acos, degrees
kernel = np.ones((5,5),np.uint8)
img1 = cv2.imread('input1.jpg')
img = cv2.imread('input1.jpg',0)
gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY)
# Create mask
height,width = img.shape
#height=height-10
#width=width-10
mask = np.zeros((height,width), np.uint8)
edges = cv2.Canny(thresh, 100, 200)
#cv2.imshow('detected ',gray)
cimg=cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
#circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1.2, 1000, param1 = 50, param2 = 30, minRadius = 20, maxRadius = 0)
for i in circles[0,:]:
i[2]=i[2]+4
# Draw on mask
cv2.circle(mask,(i[0],i[1]),i[2],(255,255,255),thickness=-1)
# Copy that image using that mask
masked_data = cv2.bitwise_and(img1, img1, mask=mask)
# Apply Threshold
_,thresh = cv2.threshold(mask,1,255,cv2.THRESH_BINARY)
# Find Contour
contours = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
x,y,w,h = cv2.boundingRect(contours[0])
# Crop masked_data
crop = masked_data[y+30:y+h-30,x+30:x+w-30]
i=crop
height, width, channels = i.shape
print (width, height, channels)
#########################################################################
ret, mask = cv2.threshold(i, 10, 255, cv2.THRESH_BINARY)
edges = cv2.Canny(i,100,200)
kernel = np.ones((11,11),np.uint8)
kernel2 = np.ones((13,13),np.uint8)
edges = cv2.dilate(edges,kernel,iterations = 1)
edges = cv2.erode(edges,kernel2,iterations = 1)
minLineLength = 1000
maxLineGap = 10
lines = cv2.HoughLinesP(edges,1,np.pi/180,15,minLineLength,maxLineGap)
h=[]
xmax1=0
xmax2=0
ymax1=0
ymax2=0
xs1=0
xs2=0
ys1=0
ys2=0
for line in lines:
x1, y1, x2, y2 = line[0]
#cv2.line(i, (x1, y1), (x2, y2), (0, 255, 0), 1)
dx=x2-x1
if(dx<0):
dx=dx*-1
dy=y2-y1
if(dy<0):
dy=dy*-1
hypo=sqrt(dx**2 + dy**2)
#print("dx=",dx," dy=",dy)
h.append(hypo)
#print(h)
print(len(h))
a=len(h)
h.sort(reverse=True)
#print(h)
m=0
k=0
for f in range(a):
for line in lines:
x1, y1, x2, y2 = line[0]
#cv2.line(i, (x1, y1), (x2, y2), (0, 255, 0), 3)
dx=x2-x1
if(dx<0):
dx=dx*-1
dy=y2-y1
if(dy<0):
dy=dy*-1
hypo2=sqrt(dx**2 + dy**2)
if(hypo2==h[0]):
m=hypo2
xmax1=x1
xmax2=x2
ymax1=y1
ymax2=y2
cv2.line(crop, (xmax1, ymax1), (xmax2, ymax2), (255, 0, 0), 3)
#print("xmax1=",xmax1," ymax1=",ymax1," xmax2=",xmax2," ymax2=",ymax2)
if(m==h[0]):
if(hypo2==h[f]):
if((sqrt((xmax2-x2)**2 + (ymax2-y2)**2))>20):
if((sqrt((xmax1-x1)**2 + (ymax1-y1)**2))>20):
xs1=x1
xs2=x2
ys1=y1
ys2=y2
cv2.line(crop, (xs1, ys1), (xs2, ys2), (0, 255, 0), 3)
print("xs1=",xs1," ys1=",ys1," xs2=",xs2," ys2=",ys2)
k=1
break
if(k==1):
break
print("xmax1=",xmax1," ymax1=",ymax1," xmax2=",xmax2," ymax2=",ymax2)
I have separated the minute's hand and hours hand in the above line of code but i need to separate the seconds hand too, Kindly anyone help me with it!
Based on this post: How to detect lines in OpenCV? I have adapted with your image and your crop method, it gives a valid output of the given image :
import cv2
import numpy as np
from matplotlib import pyplot as plt
kernel = np.ones((5,5),np.uint8)
img1 = cv2.imread('clock.jpg')
img = cv2.imread('clock.jpg',0)
gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY)
# Create mask
height,width = img.shape
mask = np.zeros((height,width), np.uint8)
edges = cv2.Canny(thresh, 100, 200)
#cv2.imshow('detected ',gray)
cimg=cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
for i in circles[0,:]:
i[2]=i[2]+4
# Draw on mask
cv2.circle(mask,(i[0],i[1]),i[2],(255,255,255),thickness=-1)
# Copy that image using that mask
masked_data = cv2.bitwise_and(img1, img1, mask=mask)
# Apply Threshold
_,thresh = cv2.threshold(mask,1,255,cv2.THRESH_BINARY)
# Find Contour
contours, hierarchy =
cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
x,y,w,h = cv2.boundingRect(contours[0])
# Crop masked_data
crop = masked_data[y+30:y+h-30,x+30:x+w-30]
################################
kernel_size = 5
blur_crop = cv2.GaussianBlur(crop,(kernel_size, kernel_size),0)
low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_crop, low_threshold, high_threshold)
rho = 1 # distance resolution in pixels
theta = np.pi / 180 # angular resolution in radians
threshold = 15 # minimum number of votes
min_line_length = 100 # minimum number of pixels making up a line
max_line_gap = 10 # maximum gap in pixels between connectable
line segments
line_image = np.copy(crop) * 0
# Run Hough on edge detected image
# Output "lines" is an array containing endpoints of detected line
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
min_line_length, max_line_gap)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),5)
# Draw the lines on the image
lines_edges = cv2.addWeighted(crop, 0.8, line_image, 1, 0)
cv2.imshow('line_image', line_image)
cv2.imshow('crop', crop)
With some parameter tweaking on the Hough detection you should be able to reduce the results to 3 nice lines.

Human fingernail segmanation without training a model

I want to mask human fingernails (fingernails white and everything including the hand is black). I do simple image operations then Canny edge detection after I smoothen the image then find contours to give internal contours white color which would be fingernails.
My problem is that when fingernails are painted it is quite easy to detect however when there is no paint it becomes really complicated and the program has to get 50 images and save outputs to a certain folder.
I am confused about how to proceed, if anybody did something similar I would appreciate some help.
import cv2
import numpy as np
import matplotlib.pyplot as plt
def display_img(img):
fig = plt.figure(figsize = (12,10))
ax = fig.add_subplot(111)
plt.imshow(img,cmap='gray')
img = cv2.imread('nail2.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
blur = cv2.blur(gray,ksize=(1,1))
kernel = np.ones((5,5),np.uint8)
display_img(blur)
med = np.median(gray)
gradient = cv2.Laplacian(blur,cv2.CV_64F)
gradient = cv2.convertScaleAbs(gradient)
plt.imshow(gradient,'gray')
lower = int(max(0,0.7*med))
upper = int(min(255,1.3*med))
edges = cv2.Canny(blur,lower,upper)
display_img(edges)
edges = cv2.GaussianBlur(edges, (11, 11), 0) # smoothing before applying threshold
display_img(edges)
image, contours, hierarchy = cv2.findContours(edges, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# Create empty array to hold internal contours
image_internal = np.zeros(image.shape)
# Iterate through list of contour arrays
for i in range(len(contours)):
# If third column value is NOT equal to -1 than its internal
if hierarchy[0][i][3] != -1:
# Draw the Contour
cv2.drawContours(image_internal, contours, i, 255, -1)
display_img(image_internal)
below is a good result:
some bad result even though fingers have pink paint:
Well, you have a big light and scale problem in these two images. But a possible solution is to segment the color channels and look for blobs.
Then you can segment with blob params.
The code you can try here:
import cv2
import numpy as np
fra = cv2.imread('nails.png')
height, width, channels = fra.shape
src = cv2.medianBlur(fra, 21)
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV_FULL)
mask = cv2.inRange(hsv, np.array([0, 0, 131]), np.array([62, 105, 255]))
mask = cv2.erode(mask, None, iterations=8)
mask = cv2.dilate(mask, None, iterations=8)
params = cv2.SimpleBlobDetector_Params()
params.filterByArea = True
params.minArea = int((height * width) / 500)
params.maxArea = int((height * width) / 10)
params.filterByCircularity = True
params.minCircularity = 0.5
params.filterByConvexity = True
params.minConvexity = 0.5
params.filterByInertia = True
params.minInertiaRatio = 0.01
detector = cv2.SimpleBlobDetector_create(params)
key_points = detector.detect(255 - mask)
vis = cv2.bitwise_and(hsv, hsv, mask=mask)
vis = cv2.addWeighted(src, 0.2, vis, 0.8, 0)
cv2.drawKeypoints(vis, key_points, vis, (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
for kp in key_points:
cv2.drawMarker(vis, (int(kp.pt[0]), int(kp.pt[1])), color=(0, 255, 0), markerType=cv2.MARKER_CROSS, thickness=3)
cv2.imshow("VIS", vis)
cv2.imwrite('nails_detected.png', vis)
cv2.waitKey(0)
cv2.destroyAllWindows()
Good luck!

how to calculate distance between 2 red pixel

I have a video file with 2 dot laser and I want to calculate the distance in pixels between them, I tried this code with OpenCV, but it is not working :
Image
import cv2
import numpy as np
cap = cv2.VideoCapture('D:\Books\Pav Man\PICS\Test\VID_20200609_195155.mp4')
#cap = cv2.VideoCapture(0)
old = 0
while (1):
# Take each frame
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_red = np.array([0, 0, 255])
upper_red = np.array([255, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)
cv2.imshow('mask', mask)
# cv2.imshow('Track Laser', frame)
moments = cv2.moments(hsv[:, :, 2])
output = cv2.connectedComponentsWithStats(mask, 8, cv2.CV_32S)
print (output[3])
print ("----**----")
if moments["m00"] > 0:
x = (moments['m10']/ moments['m00'])
y = (moments['m01']/ moments['m00'])
#print(moments['m00'],moments['m01'],moments['m10'])
#print(x, y)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
this code output = cv2.connectedComponentsWithStats(mask, 8, cv2.CV_32S) give me the centroid of points , but how to geat each point(laser dot) separately ? if I get the centroid I can measure the distance between these points
Here is your other way to do that in Python/OpenCV using connectedComponentsWithStats.
Read the input
Set min and max red colors
Use cv2.inRange() to threshold on the color range
Apply morphology open and close to clean up small spots and holes
Process with connectedComponentsWithStats to get the labels and centroids
Extract all the areas corresponding to the labels and centroids
For all labels, test if the area is smaller than some estimate of the background, as we want to exclude that centroid. Draw the label as a filled yellow region on a copy of the input. Extract the centroids of small regions and store in pts array. Draw the centroids as small green squares on the copy of the input.
For all saved points, compute the distance between successive ones.
Save the output
Input:
import cv2
import numpy as np
import math
# read image
frame = cv2.imread('red_spots.jpg')
hh, ww = frame.shape[:2]
# convert to hsv hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# threshold image
lower_red = np.array([0, 0, 225])
upper_red = np.array([255, 255, 255])
thresh = cv2.inRange(hsv, lower_red, upper_red)
# apply close and open morphology to smooth
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)
# do connected components processing
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(morph, None, None, None, 8, cv2.CV_16U)
# get all areas from stats[label_start_id:label_stop_id, area_flag]
areas = stats[0:, cv2.CC_STAT_AREA]
# draw labels and get centroids and draw centroids
result = frame.copy()
pts = []
for i in range(0, nlabels):
if areas[i] <= ww*hh/5 :
# labels start at 1 not 0
result[labels == i+1] = (0,255,255)
pt = centroids[i]
pts.append(pt)
cx = pt[0]
cy = pt[1]
x = int(round(cx))
y = int(round(cy))
# draw small square at centroids
result[y-2:y+3,x-2:x+3] = (0,255,0)
print('centroid =',cx,",",cy)
number = len(pts)
for i in range(number-1):
pt1 = pts[i]
x1 = pt1[0]
y1 = pt1[1]
pt2 = pts[i+1]
x2 = pt2[0]
y2 = pt2[1]
dist = math.sqrt( (x2-x1)**2 + (y2-y1)**2 )
print('distance =', dist)
print('')
#save images
cv2.imwrite('red_spots_thresh2.jpg',thresh)
cv2.imwrite('red_spots_morph2.jpg',morph)
cv2.imwrite('red_spots_centroids2.jpg',result)
# show images
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Morphology cleaned image:
Result image with colored region labels and centroids:
Centroids and Distances:
centroid = 1006.7307283673711 , 433.70499350726004
centroid = 1036.418693371483 , 750.4024797329519
distance = 318.08595229553544
Distance is the square root of the sum of the squares of the x difference and the y difference. So
import math
dist = math.sqrt( (x1-x2)**2 + (y1-y2)**2 )
for points x1,y1 and x2,y2
Here is one way to do your processing in Python/OpenCV using contours.
Read the input
Set min and max red colors
Use cv2.inRange() to threshold on the color range
Apply morphology open and close to clean up small spots and holes
Find contours
For each contour, draw the contour, use moments to compute the centroid, save the centroids in a list and draw a small square at the centroid location
For each successive pair of centroid points, compute the distance between them in pixels
Save the results
Input:
import cv2
import numpy as np
import math
# read image
frame = cv2.imread('red_spots.jpg')
# convert to hsv hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# threshold image
lower_red = np.array([0, 0, 225])
upper_red = np.array([255, 255, 255])
thresh = cv2.inRange(hsv, lower_red, upper_red)
# apply close and open morphology to smooth
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)
# draw contours and get centroids
spots = frame.copy()
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
pts = []
count = 0
for c in contours:
cv2.drawContours(spots, [c], -1, (0,255,0), 2)
M = cv2.moments(c)
cx = M["m10"] / M["m00"]
cy = M["m01"] / M["m00"]
pt = (cx,cy)
pts.append(pt)
x = round(cx)
y = round(cy)
# draw small square at centroids
spots[y-2:y+3,x-2:x+3] = (255,0,0)
print('centroid =',cx,",",cy)
count = count + 1
for i in range(count-1):
pt1 = pts[i]
x1 = pt1[0]
y1 = pt1[1]
pt2 = pts[i+1]
x2 = pt2[0]
y2 = pt2[1]
dist = math.sqrt( (x2-x1)**2 + (y2-y1)**2 )
print('distance =', dist)
print('')
#save images
cv2.imwrite('red_spots_thresh.png',thresh)
cv2.imwrite('red_spots_morph.png',morph)
cv2.imwrite('red_spots_centroids.png',spots)
# show images
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("spots", spots)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Morphology cleaned image:
Contours and centroids image:
Centroids and Distances:
centroid = 1036.4038142620232 , 750.3941127694858
centroid = 1006.6605586230609 , 433.9662237323787
distance = 317.8227024875417
You can do this:
The function cv2.InRange() to find two red dots.
The function cv2.connectedComponentsWithStats() to find the centroids of these red points.
Calculate the Euclidean distance between centroids.
Also, you can select points by brightness only, without even using their color.
import cv2
img = cv2.imread('HAgbc.jpg')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.normalize(gray, gray, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC1)
points=cv2.threshold(gray, 230, 255, cv2.THRESH_BINARY )[1]
output = cv2.connectedComponentsWithStats(points, 8, cv2.CV_32S)
centroids = output[3]
x,y=(centroids[1]-centroids[2])
dist=cv2.magnitude(x, y)[0]
print('distance is: ', *dist)
Or this code (find two brightness maximum):
import cv2
img = cv2.imread('HAgbc.jpg')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
pos1=cv2.minMaxLoc(gray)[3]
cv2.circle(gray, pos1, 30, 0, -1) # masking first spot
pos2=cv2.minMaxLoc(gray)[3]
x=pos1[0]-pos2[0]
y=pos1[1]-pos2[1]
dist=cv2.magnitude(x, y)[0]
print('distance is: ', *dist)

Is there any way to crop an image inside a box?

I want to crop the image only inside the box or rectangle. I tried so many approaches but nothing worked.
import cv2
import numpy as np
img = cv2.imread("C:/Users/hp/Desktop/segmentation/add.jpeg", 0);
h, w = img.shape[:2]
# print(img.shape)
kernel = np.ones((3,3),np.uint8)
img2 = img.copy()
img2 = cv2.medianBlur(img2,5)
img2 = cv2.adaptiveThreshold(img2,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
img2 = 255 - img2
img2 = cv2.dilate(img2, kernel)
img2 = cv2.medianBlur(img2, 9)
img2 = cv2.medianBlur(img2, 9)
cv2.imshow('anything', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
position = np.where(img2 !=0)
x0 = position[0].min()
x1 = position[0].max()
y0 = position[1].min()
y1 = position[1].max()
print(x0,x1,y0,y1)
result = img[x0:x1,y0:y1]
cv2.imshow('anything', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output should be the image inside the sqaure.
You can use contour detection for this. If your image has basically only a hand drawn rectangle in it, I think it's good enough to assume it's the largest closed contour in the image. From that contour, we can figure out a polygon/quadrilateral approximation and then finally get an approximate rectangle. I'll define some utilities at the beginning which I generally use to make my time easier when messing around with images:
def load_image(filename):
return cv2.imread(filename)
def bnw(image):
return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
def col(image):
return cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
def fixrgb(image):
return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
def show_image(image, figsize=(7,7), cmap=None):
cmap = cmap if len(image.shape)==3 else 'gray'
plt.figure(figsize=figsize)
plt.imshow(image, cmap=cmap)
plt.show()
def AdaptiveThresh(gray):
blur = cv2.medianBlur(gray, 5)
adapt_type = cv2.ADAPTIVE_THRESH_GAUSSIAN_C
thresh_type = cv2.THRESH_BINARY_INV
return cv2.adaptiveThreshold(blur, 255, adapt_type, thresh_type, 11, 2)
def get_rect(pts):
xmin = pts[:,0,1].min()
ymin = pts[:,0,0].min()
xmax = pts[:,0,1].max()
ymax = pts[:,0,0].max()
return (ymin,xmin), (ymax,xmax)
Let's load the image and convert it to grayscale:
image_name = 'test.jpg'
image_original = fixrgb(load_image(image_name))
image_gray = 255-bnw(image_original)
show_image(image_gray)
Use some morph ops to enhance the image:
kernel = np.ones((3,3),np.uint8)
d = 255-cv2.dilate(image_gray,kernel,iterations = 1)
show_image(d)
Find the edges and enhance/denoise:
e = AdaptiveThresh(d)
show_image(e)
m = cv2.dilate(e,kernel,iterations = 1)
m = cv2.medianBlur(m,11)
m = cv2.dilate(m,kernel,iterations = 1)
show_image(m)
Contour detection:
contours, hierarchy = cv2.findContours(m, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
total_area = np.prod(image_gray.shape)
max_area = 0
for cnt in contours:
# Simplify contour
perimeter = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.03 * perimeter, True)
area = cv2.contourArea(approx)
# Shape is recrangular, so 4 points approximately and it's convex
if (len(approx) == 4 and cv2.isContourConvex(approx) and max_area<area<total_area):
max_area = cv2.contourArea(approx)
quad_polygon = approx
img1 = image_original.copy()
img2 = image_original.copy()
cv2.polylines(img1,[quad_polygon],True,(0,255,0),10)
show_image(img1)
tl, br = get_rect(quad_polygon)
cv2.rectangle(img2, tl, br, (0,255,0), 10)
show_image(img2)
So you can see the approximate polygon and the corresponding rectangle, using which you can get your crop. I suggest you play around with median blur and morphological ops like erosion, dilation, opening, closing etc and see which set of operations suits your images the best; I can't really say what's good from just one image. You can crop using the top left and bottom right coordinates:
show_image(image_original[tl[1]:br[1],tl[0]:br[0],:])
Draw the square with a different color (e.g red) so it can be distinguishable from other writing and background. Then threshold it so you get a black and white image: the red line will be white in this image. Get the coordinates of white pixels: from this set, select only the two pairs (minX, minY)(maxX,maxY). They are the top-left and bottom-right points of the box (remember that in an image the 0,0 point is on the top left of the image) and you can use them to crop the image.

Python: Showing every Object of an image in its own window

I've written some code, to crop an object (in this case the Data Matrix Code) from an image:
import numpy as np
import cv2
image = cv2.imread("datamatrixc.png")
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
img_height, img_width = image.shape[:2]
WHITE = [255, 255, 255]
# Threshold filter
ret, thresh = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
# Get Contours
_, contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Get Last element of the contours object
max = len(contours) - 1
cnt = contours[max]
# Get coordinates for the bounding box
x, y, w, h = cv2.boundingRect(cnt)
image_region = image[ int(((img_height / 2) - h) / 2) : int(((img_height / 2) - h) / 2 + h), int(x): int(x + w) ]
dmc = cv2.copyMakeBorder(image_region, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value = WHITE)
cv2.imshow("Test", dmc)
cv2.waitKey(0)
cv2.destroyAllWindows()
The code works fine and I received as result:
However, the next image is a little more complicated.
I receive the same result as in the previous image, but I have no idea how to detect the two other objects.
Is there an easier way every object showing in its window?
For this specific image take the biggest contours you have and check if the object is 4 sided shape.If the half-point between the bounding box's corners (see pairs below) is in the contour array then voila, problem solved.
Pairs : TopRight-TopLeft, TopRight-BottomRight, TopLeft-BottomLeft, BottomLeft-BottomRight
Or you could check if there pixels that are not black/white inside the bounding box ?
And for the ploting individualy just slap a for on what you allready have
How about this?
import numpy as np
import cv2
image = cv2.imread("datamatrixc.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, bin_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
closing = cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel, iterations=4)
n_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(bin_img)
size_thresh = 5000
for i in range(1, n_labels):
if stats[i, cv2.CC_STAT_AREA] >= size_thresh:
print(stats[i, cv2.CC_STAT_AREA])
x = stats[i, cv2.CC_STAT_LEFT]
y = stats[i, cv2.CC_STAT_TOP]
w = stats[i, cv2.CC_STAT_WIDTH]
h = stats[i, cv2.CC_STAT_HEIGHT]
cv2.imshow('img', image[y:y+h, x:x+w])
cv2.waitKey(0)

Categories