2017-12-22 64 views
3

私は以下のイメージを取って、白い形をトレースし、結果のパスをpdfにエクスポートしようとしています。私が持っている問題は、findContoursが見えるのは、形状の端に沿った点しか見つけられないということです。 findContoursと同様に、形状の曲線を検出し、曲線があるところでその点をスプラインで置き換えるソリューションがありますか?私がscipy.interpolateを使用すると、直線を無視し、輪郭全体を一つの大きな湾曲した形に変えます。私は両方のことをする何かが必要です。OpenCV findContoursと同様の機能があり、カーブを検出してポイントをスプラインに置き換えますか?

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) 
+0

この[スレッド](https://stackoverflow.com/questions/31464345/fitting-a-closed-curve-to-a-set-of-points)は役に立ちますか? – ZdaR

+0

そのスレッド@ ZdaRのソリューションは、パターン全体を丸くします。私はopが外側の端に直線を探し、必要なところで湾曲していると思う。 –

+0

1つの解決策は、凸包を構成する輪郭の点を取ることであり、これは曲線に適合する必要のない点に対応する。左の点は曲線からの点になり、その部分だけにスプラインを当てることができます。 –

答えて

2

私はあなたの問題は、実際には2つの問題で構成さだと思います。

最初の問題は、TEH 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

私は完全にかかわらず、あなたの第2工程を理解していない可能性があります。ポイントリストの一部をスプラインで置き換えて、それらのポイントのいくつかを省略したいとします。あなたの最終的な意図に応じて、これを行う別の方法があるかもしれません。直線を置き換えるだけですか?曲がった部分を交換すると、許容している誤差の範囲は?

+0

非常に丁寧な対応をありがとう。私はあなたが楽しいホリデーシーズンを過ごしたことを願っています。パスの曲線をスプラインで置き換えたい誤差の限りでは、スプラインが95%を超えるようにしたいと思います。複数のスプラインが問題ありません。あなたの回答は、輪郭内のすべての直線を検出し、それらの点を省略し、残りの点にscipy.interpolateを適用し、最後にすべての点を接続するのが良いアプローチだと考えています。 – statequarter

0
# 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() 
+0

あなたの答えに説明を追加してください、郵便番号だけでなく – zgue

1

輪郭を約するためのフラグcv2.CHAIN_APPROX_SIMPLEcv2.findContoursを使用したことを除いて、我々はそれを手動で行うことができます。

  1. cv2.findContoursとフラグcv2.CHAIN_APPROX_NONEを使用して輪郭を見つける。
  2. cv2.arcLengthを使用して輪郭長を計算します。
  3. cv2.approxPoolyDPを約epsilon = eps * arclenで手動で輪郭に近づけます。

    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, hier = cv2.findContours(threshed,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE) 
    
    # 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) 
    
    ここ

結果eps=0.005の一つであります

関連する問題