是否有类似于OpenCV findContours的函数检测曲线并用样条曲线替换点?

时间:2017-12-22 05:51:25

标签: python opencv matplotlib scipy

我正在尝试拍摄下面的图像,跟踪白色形状,并将生成的路径导出为pdf。我遇到的问题是findContours似乎只能找到沿着形状边缘的点。是否存在类似于findContours的解决方案,它可以检测形状中的曲线,并在有曲线的地方用样条曲线替换其点?如果我使用scipy.interpolate它会忽略直线并将整个轮廓变成一个大的弯曲形状,这也不好。我需要能做到这两件事的事情。

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)

3 个答案:

答案 0 :(得分:2)

我认为你的问题实际上包含两个问题。

第一个问题是提取轮廓,使用findContour函数可以实现:

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)

如果执行程序,则轮廓点将打印为点坐标列表。

您选择的轮廓近似方法会影响实际使用的插值(以及找到的点数),如here所述。我在使用近似方法cv2.CHAIN_APPROX_SIMPLE找到的点处添加了小点。你看到直线已经近似了。

enter image description here

但是,我可能还没有完全理解你的第二步。您想省略其中一些点,部分用样条线替换点列表。根据您的最终意图,可能有不同的方法。你只想更换直线吗?如果更换弯曲部件,您允许的误差范围是多少?

答案 1 :(得分:1)

# import the necessary packages
import numpy as np
import argparse
import glob
import cv2
#For saving pdf 
def save_pdf(imagename):
  import img2pdf
  # opening from filename
  with open("output.pdf","wb") as f:
    f.write(img2pdf.convert(imagename))
#for fouind biggest contours
def bigercnt(contours):
  max_area=0
  cnt=[]
  for ii in contours:
    area=cv2.contourArea(ii)
    if area>max_area:
      cnt = ii
  return cnt
#STARTING
print ("Reading img.jpg file")
# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread('img.jpg')
image = cv2.resize(image, (0,0), fx=0.5, fy=0.5) 
print ("Converting it gray scale")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print ("Bluring")
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
print ("Looking for edges" )
# apply Canny edge detection using a wide threshold, tight
# threshold, and automatically determined threshold
tight = cv2.Canny(blurred, 255, 250)
print ("Looking for contours")
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
close = cv2.morphologyEx(tight, cv2.MORPH_CLOSE, kernel)
_,contours, hierarchy = cv2.findContours( close.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Looking for big contour")
cnt = bigercnt(contours)
print ("Cropping found contour")
x,y,w,h = cv2.boundingRect(cnt)
croped_image = image[y:y+h,x:x+w]
img2 = np.zeros((h,w,4),np.uint8)      
print ("Taking only pixels in countour and creating png")
for i in range(h):
    for j in range(w):
      #print (x+j, y+i)
      #print   cv2.pointPolygonTest(cnt, (x+j, y+i), False)
      if cv2.pointPolygonTest(cnt, (x+j, y+i), False)==1:    
        #print True
        img2[i,j] = [croped_image[i, j][0],croped_image[i, j][1],croped_image[i, j][2],255]
      else:
        img2[i,j] = [255,255,255,0]
print ("Showing output image")
# Show the output image
#cv2.imshow('croped', croped_image)
cv2.imshow('output', img2)
params = list()
params.append(cv2.IMWRITE_PNG_COMPRESSION)
params.append(8)
print ("Saving output image")
cv2.imwrite("output.png",img2,params)
print ("Finish:converted")
cv2.waitKey(0)
cv2.destroyAllWindows()

答案 2 :(得分:1)

除了使用带有cv2.findContours标志的cv2.CHAIN_APPROX_SIMPLE大致轮廓外,我们可以手动完成。

  1. 使用cv2.findContours与标记cv2.CHAIN_APPROX_NONE查找轮廓。
  2. 使用cv2.arcLength计算轮廓长度。
  3. 使用cv2.approxPoolyDP手动使用epsilon = eps * arclen来近似轮廓。
  4. 以下是eps=0.005

    时的结果之一

    enter image description here

    更多结果:

    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)