python&openCVで点つなぎの自動生成

# 初めに
今回は画像から点つなぎを自動生成するプログラムを作りました。

# 方法

①まず画像を用意します。


![test.png](https://qiita-image-store.s3.amazonaws.com/0/206651/a28196fb-b79b-9fbc-e8c1-76eb9f02d4f9.png)

②線画にします

![d.png](https://qiita-image-store.s3.amazonaws.com/0/206651/9fee972b-4b72-7ffa-01bf-e86d03c9b867.png)

③線画に沿って点を書きます。

![d2.png](https://qiita-image-store.s3.amazonaws.com/0/206651/e91256e3-e6b4-0c71-5ad4-1883838ea838.png)

④点が近い順に点に番号を振ります。

![out.png](https://qiita-image-store.s3.amazonaws.com/0/206651/89e01eaf-7e4f-9ca4-1194-8b56f2ee9f70.png)

本来ならば近い順ではなくて線に沿って点の番号を振らなければなりませんが、そうすると大変そうなので取り敢えずこの方式で。

[お借りした素材](http://www.irasutoya.com/2012/11/blog-post.html)

# ソースコード

pythonで書いていきます。

```python3:main.py
import cv2
import numpy as np
import sys
import math
import copy

args=sys.argv


def make_contour_image(path):
    neiborhood24 = np.array([[1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1]],
                             np.uint8)
    # グレースケールで画像を読み込む.
    gray = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

    # 白い部分を膨張させる.
    dilated = cv2.dilate(gray, neiborhood24, iterations=1)

    # 差をとる.
    diff = cv2.absdiff(dilated, gray)

    # 白黒反転
    contour = 255 - diff
    return contour

rect_size=5

def rect_set_func(image,x,y):
     for ey in range(max(y-rect_size//2,0),min(y+rect_size//2,image.shape[0])):
         for ex in range(max(x-rect_size//2,0),min(x+rect_size//2,image.shape[1])):
             image[ey,ex]=0
     return image

BN=200
N=70

def my_search_func(NX,NY,X,Y):
         if len(X)==0:
               return True
         return len([None for x,y in zip(X,Y) if math.sqrt((NX-x)**2+(NY-y)**2)<N])==0

def my_search_func3(a,d):
         if len(d)==0:return True
         return len([None for e in d if a==e])==0

def my_search_func4(a,x,y,A):
     temp1=copy.copy(x)
     temp2=copy.copy(y)

     for e in A:
         temp1=rm_array_Get(temp1,e)
         temp2=rm_array_Get(temp2,e)
     A=[[i,math.sqrt((a[0]-ex)**2+(a[1]-ey)**2)] for i,ex,ey in zip(range(len(x)),temp1,temp2)]
     A.sort(key=lambda x: x[1])
#     A.reverse()
     return A[0][0]


def my_search_func2(X,Y):
     I=0
     ND=[]
     A=[]
     for i in range(0,len(X)):
          A.append(I)
          I=my_search_func4([X[I],Y[I]],X,Y,A)
     return A

def rm_array_Get(a,I):     
     return [e if i!=I else 100000000000 for i,e in enumerate(a)]



def main():
    image_data=make_contour_image(args[1])
    out_image_data=np.full(image_data.shape,255).astype(np.uint8)
    TX=[]
    TY=[]
    ND=[] 
       
    cv2.imwrite("d.png", image_data)

    for ey in range(image_data.shape[0]):
         for ex in range(image_data.shape[1]):
 #             print(image_data[ey,ex])
#              print(my_search_func(ex,ey,TX,TY))
              if my_search_func(ex,ey,TX,TY) and image_data[ey,ex]<BN:
                      out_image_data=rect_set_func(out_image_data,ex,ey)
                      TY.append(ey)
                      TX.append(ex)
    
    ND=my_search_func2(TX,TY)
    
    cv2.imwrite("d2.png", out_image_data)
    
 
    print(ND)              
    I=0
    for i in range(len(ND)):
            cv2.putText(out_image_data, str(i), 
                (TX[ND[i]]+rect_size, TY[ND[i]]+rect_size), 
                cv2.FONT_HERSHEY_PLAIN, 1, 
                (0,0,0), 1, cv2.LINE_AA)
            
    
    cv2.imwrite(args[2], out_image_data)
   
main()
```

[make_contour_imageの部分の参考](http://www.mathgram.xyz/entry/cv/contour)

## 解説等

まず、openCVで線画を作ります。

```python3
image_data=make_contour_image(args[1])
```

その線画に従って点を書いていきます。

```python3
    for ey in range(image_data.shape[0]):
         for ex in range(image_data.shape[1]):
 #             print(image_data[ey,ex])
#              print(my_search_func(ex,ey,TX,TY))
              if my_search_func(ex,ey,TX,TY) and image_data[ey,ex]<BN:
                      out_image_data=rect_set_func(out_image_data,ex,ey)
                      TY.append(ey)
                      TX.append(ex)
```

ただし、点と点との間隔はN(デフォルト70ピクセル)です。
その後、my_search_func2で点ごとに順番を決めて番号を振ります。

```python3
ND=my_search_func2(TX,TY)
```

最後にNDのデータに従って番号を画像に書き込みます。

```python3
    I=0
    for i in range(len(ND)):
            cv2.putText(out_image_data, str(i), 
                (TX[ND[i]]+rect_size, TY[ND[i]]+rect_size), 
                cv2.FONT_HERSHEY_PLAIN, 1, 
                (0,0,0), 1, cv2.LINE_AA)
```

# 最後に

今回のソースコードだと一応点つなぎは生成できるものの人間が作ったものには遠く及びません。