Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a function similar to OpenCV findContours that detects curves and replaces points with a spline?

I am trying to take the below image, trace the white shape, and export the resulting path to pdf. The problem I have is that findContours seeming only finds points along the edge of the shape. Is there a solution out there, similar to findContours, that detects curves in a shape and replaces its points with a spline wherever there is a curve? If I use scipy.interpolate it ignores straight lines and turns the entire contour into one big curved shape, which is no good either. I need something that does both things.

import numpy as np
import cv2
from scipy.interpolate import splprep, splev
from pyx import *
import matplotlib.pyplot as plt

#read in image file                                                             
original = cv2.imread('test.jpg')

#blur the image to smooth edges                                                 
im = cv2.medianBlur(original,5)

#threshold the image                                                            
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,170,255,cv2.THRESH_BINARY)                                                             

#findContours                                                                   
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_\
APPROX_SIMPLE)

#drawContours
cv2.drawContours(original, [approx], -1, (0,255,0), 3)                          
cv2.imshow("Imageee", original)                                                 
cv2.waitKey(0)
like image 600
statequarter Avatar asked Dec 22 '17 05:12

statequarter


2 Answers

Except using cv2.findContours with flag cv2.CHAIN_APPROX_SIMPLE to approx the contours, we can do it manually.

  1. use cv2.findContours with flag cv2.CHAIN_APPROX_NONE to find contours.
  2. use cv2.arcLength to calculate the contour length.
  3. use cv2.approxPoolyDP to approx the contour manually with epsilon = eps * arclen.

Here is one of the results when eps=0.005:

enter image description here

More results:

enter image description here


#!/usr/bin/python3
# 2018.01.04 13:01:24 CST
# 2018.01.04 14:42:58 CST

import cv2
import numpy as np
import os
img = cv2.imread("test.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,threshed = cv2.threshold(gray,170,255,cv2.THRESH_BINARY)

# find contours without approx
cnts = cv2.findContours(threshed,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)[-2]

# get the max-area contour
cnt = sorted(cnts, key=cv2.contourArea)[-1]

# calc arclentgh
arclen = cv2.arcLength(cnt, True)

# do approx
eps = 0.0005
epsilon = arclen * eps
approx = cv2.approxPolyDP(cnt, epsilon, True)

# draw the result
canvas = img.copy()
for pt in approx:
    cv2.circle(canvas, (pt[0][0], pt[0][1]), 7, (0,255,0), -1)

cv2.drawContours(canvas, [approx], -1, (0,0,255), 2, cv2.LINE_AA)

# save
cv2.imwrite("result.png", canvas)
like image 95
Kinght 金 Avatar answered Nov 15 '22 09:11

Kinght 金


I think your problem actually consists of two issues.

The first issue is to extract the contour, which you can achieve using teh findContour function:

import numpy as np

print cv2.__version__

rMaskgray = cv2.imread('test.jpg', 0)
(thresh, binRed) = cv2.threshold(rMaskgray, 200, 255, cv2.THRESH_BINARY)

_, Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.argmax(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255

contour= Rcontours[max_rarea]
cv2.drawContours(CntExternalMask,[contour],-1,0,1)
print "These are the contour points:"
print c
print
print "shape: ", c.shape

for p in contour:
    print p[0][0]
    cv2.circle(CntExternalMask, (p[0][0], p[0][1]), 5, (0,255,0), -1)

cv2.imwrite("contour.jpg", CntExternalMask)
cv2.imshow("Contour image", CntExternalMask)                                                 
cv2.waitKey(0)

If you execute the program, the contour points are printed as a list of point coordinates.

The contour approximation method you choose influences the interpolation which is actually used (and the number of points found), as described here. I have added small dots at the points found with the approximation method cv2.CHAIN_APPROX_SIMPLE. You see that the straight lines are already approximated.

enter image description here

I may not fully have understood your second step, though. You want to omit some of those points, replacing point lists partially by splines. There might be different way to do this, depending on your final intention. Do you just want to replace the straight lines? If you replace curved parts, what is the margin of error you are allowing?

like image 32
tfv Avatar answered Nov 15 '22 07:11

tfv