Remove key points from edges of an object - python

I am working with images with objects in it. I used canny edge detection and contours to detect and draw the edges of the objects in it. Then I used both SIFT and SURF to detect key points in the object. Here is the sample code I've been working on.
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread(image)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 100,200)
image, contours, hierarchy = cv.findContours(edges, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
outimg = cv.drawContours(img, contours, -1, (0,255,0), 3)
sift = cv.xfeatures2d_SIFT.create()
kp, des = sift.detectAndCompute(outimg,None)
Is there any way to remove the key points that are on the edge? Answer with example will be really helpful.
Thanks.

You can use pointPolygonTest method to filter detected keypoints. Use detected contours as boundary polygon. You will also be able to define desired margin.
Simaple example (fot 4 point contour):
def inside_point(self, point, rect):
# point is a list (x, y)
# rect is a contour with shape [4, 2]
rect = rect.reshape([4, 1, 2]).astype(np.int64)
dist = cv2.pointPolygonTest(rect,(point[0], point[1]),True)
if dist>=0:
# print(dist)
return True
else:
return False
You can also draw contours on mask image and to check if the point is inside contours, just check the pixel value with the point coordinates, and if it not 0 then point is valid.
Seems everythig woeks fine:
I have no xfeatures2d, so used ORB features here.
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('image.jpg')
#img = cv.resize(img,(512,512))
img = cv.copyMakeBorder(img,20,20,20,20, 0)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
_ , gray = cv.threshold(gray,20,255,cv.THRESH_TOZERO)
gray=cv.erode(gray,np.ones( (5,5), np.int8) )
edges = cv.Canny(gray, 100,200)
contours, hierarchy = cv.findContours(edges, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
orb = cv.ORB_create(nfeatures=10000)
kp, des = orb.detectAndCompute(gray,None)
outimg = cv.drawContours(img, contours, -1, (0,255,0), 3)
k = []
for cont in contours:
for i in kp:
(x, y) =i.pt
dist = cv.pointPolygonTest(cont, (x,y), True)
if dist>=0:
k.append(i)
for i in k:
pt=(int(i.pt[0]),int(i.pt[1]) )
cv.circle(outimg,pt,3, (255,255,255),-1)
cv.imwrite('result.jpg',outimg)
cv.imshow('outimg',outimg)
cv.waitKey()

I am still finding it difficult to remove key points from the given image. I tried to append the key points in a new list if it's not in the contour but shows an error when I use cv2.drawKeypoints function because the new list is not of type keypoint. This is the work I have done so far.
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread(image)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 100,200)
image, contours, hierarchy = cv.findContours(edges, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
outimg = cv.drawContours(img, contours, -1, (0,255,0), 3)
sift = cv.xfeatures2d_SIFT.create()
kp, des = sift.detectAndCompute(outimg,None)
k = cv.KeyPoint()
for i in kp:
(x, y) =i.pt
dist = cv.pointPolygonTest(contours[0], (x,y), True)
if dist>=0:
k1.append(k)

Related

OpenCv: Connect edges to make rectangle

I am using opencv to detect receipt:
import numpy as np
import cv2
import matplotlib.pyplot as plt
from skimage.filters import threshold_local
from PIL import Image
import sys
file_name = "test.jpg"
image = cv2.imread(file_name)
# Downscale image as finding receipt contour is more efficient on a small image
resize_ratio = 500 / image.shape[0]
original = image.copy()
image = opencv_resize(image, resize_ratio)
# Convert to grayscale for further processing
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Get rid of noise with Gaussian Blur filter
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Detect white regions
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))
dilated = cv2.dilate(blurred, rectKernel)
# Detect edge
edged = cv2.Canny(dilated, 40, 200, apertureSize=3)
# Detect all contours in Canny-edged image
contours, hierarchy = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
image_with_contours = cv2.drawContours(image.copy(), contours, -1, (0,255,0), 3)
# Create hull to have good shape rectangle
# Find the convex hull object for each contour
hull_list = []
for i in range(len(contours)):
hull = cv2.convexHull(contours[i])
hull_list.append(hull)
image_with_hull = cv2.drawContours(image.copy(), hull_list, -1, (0,255,0), 3)
# Get 10 largest contours
#largest_contours = sorted(contours, key = cv2.contourArea, reverse = True)[:10]
largest_contours = sorted(hull_list, key = cv2.contourArea, reverse = True)[:10]
image_with_largest_contours = cv2.drawContours(image.copy(), largest_contours, -1, (0,255,0), 3)
I am detecting image contours and once I have the contours I would like to make it a rectangle to make sure I capture a complete receipt. I am using convex hull for making the rectangle, but the problem is convex hull not follow straight lines (I mean along with border of the receipt, like in 90-degree angle) instead it just connects the points where it stopped which is not correct. Is it possible to make a rectangle with 4 points to capture a complete receipt? something like this:
Any help will be really appreciated.

Is there a way to draw inscribed ellipse in a almost rectangular contour?

I am wondering if there is a way to inscribe an ellipse in almost rectangular contour? I am using openCV findcontours() method in my image and my aim is to fit biggest possible ellipses inside every object in the following image:
Code I am using:
import cv2
import numpy as np
def find_contours(preprocessed, min_perimeter):
contours, hierarchy = cv2.findContours(preprocessed,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) #find contours
contours_new = [] #new empty list of contours
for cnts in contours: #looping trough contours
perimeter = cv2.arcLength(cnts,True) #arc length
if perimeter > min_perimeter: #leave only significant contours
contours_new.append(cnts)
return contours_new
min_perimeter = 7000
img_num = 6
results_path = 'results/'
image_path = 'images/'
mask = image_path+str(img_num)+'_predicted_mask.bmp'
empty = np.empty([9452, 6144], dtype=np.uint16)
empty = cv2.cvtColor(empty, cv2.COLOR_GRAY2BGR)
gray = cv2.imread(mask)
ret,thresholded_mask = cv2.threshold(gray,150,255,0)
gray_mask = cv2.cvtColor(thresholded_mask, cv2.COLOR_BGR2GRAY)
contours_new = find_contours(gray_mask, min_perimeter)
cv2.drawContours(empty, contours_new, -1, (255,255,255), 3)
for cnt in contours_new:
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(empty, ellipse, (0, 0, 255), 3)
cv2.imwrite('contours.bmp',empty)
Result (as you can see elipses are not inscribed):

How to recolor image based on edge detection(canny)

I have a script, which is using for recoloring room walls based on color similarity. But I need to recolor a wall based on edge detection.
import cv2
import numpy as np
import sys
from PIL import Image
import numpy as np
from hex_to_rgb import color
def recolor(file_path, celor, lower_color, upper_color):
img = cv2.imread(file_path)
res = img.copy()
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
r2, g2, b2 = color(celor)
mask = cv2.inRange(rgb, lower_color, upper_color)
mask = mask/255
mask = mask.astype(np.bool)
res[:,:,:3][mask] = [b2, g2, r2] # opencv uses BGR
im_rgb = cv2.cvtColor(res, cv2.COLOR_BGR2RGB)
return im_rgb
file_path --> image
celor --> color, which you want to recolor
lower_color --> lower values of RGB
upper_color --> upper values of RGB
I am using Sobel edge detection to solve this problem. I tried with Canny edge detection also but it didn't give good results.
After edge detection, I applied the threshold to the image and found contours in the image. The problem here is that I am coloring the contour with the maximum area in this case. You will have to figure out a way to choose the contour you want to color.
img = cv2.imread("colourWall.jpg")
cImg = img.copy()
img = cv2.blur(img, (5, 5))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
scale = 1
delta = 0
ddepth = cv.CV_16S
grad_x = cv.Sobel(gray, ddepth, 1, 0, ksize=3, scale=scale, delta=delta, borderType=cv.BORDER_DEFAULT)
grad_y = cv.Sobel(gray, ddepth, 0, 1, ksize=3, scale=scale, delta=delta, borderType=cv.BORDER_DEFAULT)
abs_grad_x = cv.convertScaleAbs(grad_x)
abs_grad_y = cv.convertScaleAbs(grad_y)
grad = cv.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)
ret, thresh = cv2.threshold(grad, 10, 255, cv2.THRESH_BINARY_INV)
c, h = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
areas = [cv2.contourArea(c1) for c1 in c]
maxAreaIndex = areas.index(max(areas))
cv2.drawContours(cImg, c, maxAreaIndex, (255, 0, 0), -1)
plt.imshow(cImg)
plt.show()
Result:

OpenCV, cv2.approxPolyDP() Draws double lines on closed contour

I want to create some polygons out of this mask:
image 1 - Mask
So i created these contours with openCV findcontours():
image 2 - Contours
When creating polygons I get these polygons:
image 3 - Polygons
As you can see some polygons are drawn using double lines. How do I prevent this?
See my code:
import glob
from PIL import Image
import cv2
import numpy as np
# Let's load
image = cv2.imread(path + "BigOneEnhanced.tif")
# Grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Find Canny edges
edged = cv2.Canny(gray, 30, 200)
# Finding Contours
contours, hierarchy = cv2.findContours(edged,
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1)
canvas = np.zeros(image.shape, np.uint8)
# creating polygons from contours
polygonelist = []
for cnt in contours:
# define contour approx
perimeter = cv2.arcLength(cnt,True)
epsilon = 0.005*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
polygonelist.append(approx)
cv2.drawContours(canvas, polygonelist, -1, (255, 255, 255), 3)
imgB = Image.fromarray(canvas)
imgB.save(path + "TEST4.png")
The problem source is the Canny edges detection:
After applying edge detection you are getting two contours for every original contour - one outside the edge and one inside the edge (and other weird stuff).
You may solve it by applying findContours without using Canny.
Here is the code:
import glob
from PIL import Image
import cv2
import numpy as np
path = ''
# Let's load
#image = cv2.imread(path + "BigOneEnhanced.tif")
image = cv2.imread("BigOneEnhanced.png")
# Grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply threshold (just in case gray is not binary image).
ret, thresh_gray = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Find Canny edges
#edged = cv2.Canny(gray, 30, 200)
# Finding Contours cv2.CHAIN_APPROX_TC89_L1
#contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours, hierarchy = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
canvas = np.zeros(image.shape, np.uint8)
# creating polygons from contours
polygonelist = []
for cnt in contours:
# define contour approx
perimeter = cv2.arcLength(cnt, True)
epsilon = 0.005*perimeter #0.005*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
polygonelist.append(approx)
cv2.drawContours(canvas, polygonelist, -1, (255, 255, 255), 3)
imgB = Image.fromarray(canvas)
imgB.save(path + "TEST4.png")
Result:

How to find the center and angle of objects in an image?

I am using python and OpenCV. I am trying to find the center and angle of the batteries:
Image of batteries with random angles:
The code than I have is this:
import cv2
import numpy as np
img = cv2.imread('image/baterias2.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img2 = cv2.imread('image/baterias4.png',0)
minLineLength = 300
maxLineGap = 5
edges = cv2.Canny(img2,50,200)
cv2.imshow('Canny',edges)
lines = cv2.HoughLinesP(edges,1,np.pi/180,80,minLineLength,maxLineGap)
print lines
salida = np.zeros((img.shape[0],img.shape[1]))
for x in range(0, len(lines)):
for x1,y1,x2,y2 in lines[x]:
cv2.line(salida,(x1,y1),(x2,y2),(125,125,125),0)# rgb
cv2.imshow('final',salida)
cv2.imwrite('result/hough.jpg',img)
cv2.waitKey(0)
Any ideas to work it out?
Almost identical to one of my other answers. PCA seems to work fine.
import cv2
import numpy as np
img = cv2.imread("test_images/battery001.png") #load an image of a single battery
img_gs = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #convert to grayscale
#inverted binary threshold: 1 for the battery, 0 for the background
_, thresh = cv2.threshold(img_gs, 250, 1, cv2.THRESH_BINARY_INV)
#From a matrix of pixels to a matrix of coordinates of non-black points.
#(note: mind the col/row order, pixels are accessed as [row, col]
#but when we draw, it's (x, y), so have to swap here or there)
mat = np.argwhere(thresh != 0)
#let's swap here... (e. g. [[row, col], ...] to [[col, row], ...])
mat[:, [0, 1]] = mat[:, [1, 0]]
#or we could've swapped at the end, when drawing
#(e. g. center[0], center[1] = center[1], center[0], same for endpoint1 and endpoint2),
#probably better performance-wise
mat = np.array(mat).astype(np.float32) #have to convert type for PCA
#mean (e. g. the geometrical center)
#and eigenvectors (e. g. directions of principal components)
m, e = cv2.PCACompute(mat, mean = np.array([]))
#now to draw: let's scale our primary axis by 100,
#and the secondary by 50
center = tuple(m[0])
endpoint1 = tuple(m[0] + e[0]*100)
endpoint2 = tuple(m[0] + e[1]*50)
red_color = (0, 0, 255)
cv2.circle(img, center, 5, red_color)
cv2.line(img, center, endpoint1, red_color)
cv2.line(img, center, endpoint2, red_color)
cv2.imwrite("out.png", img)
To find out the center of an object, you can use the Moments.
Threshold the image and get the contours of the object with findContours.
Compute the Moments withcv.Moments(arr, binary=0) → moments.
As arr you can pass the contours. Then the coordinates of the center are computed as x = m10/m00 and y = m01/m00.
To get the orientation, you can draw a minimum Rectangle around the object and compute the angle between the longer side of the rectangle and a vertical line.
You can reference the code.
import cv2
import imutils
import numpy as np
PIC_PATH = r"E:\temp\Battery.jpg"
image = cv2.imread(PIC_PATH)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 100, 220)
kernel = np.ones((5,5),np.uint8)
closed = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel)
cnts = cv2.findContours(closed.copy(), 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), 4)
cv2.imshow("Output", image)
cv2.waitKey(0)
The result picture is,

Categories

Resources