Python
OpenCV
python3

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

初めに

幼児から大人まで楽しめるらしい点つなぎ。
そこで、今回は画像から点つなぎを自動生成するプログラムを作りました。

方法

①まず画像を拾ってきます。

test.png

②線画にします

d.png

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

d2.png

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

out.png

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

お借りした素材

ソースコード

pythonで書いていきます。

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の部分の参考

解説等

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

image_data=make_contour_image(args[1])

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

    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で点ごとに順番を決めて番号を振ります。

ND=my_search_func2(TX,TY)

最後に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)

最後に

今回のソースコードだと一応点つなぎは生成できるものの人間が作ったものには遠く及びません。
画像処理において点つなぎの生成は非常に面白いことだと思うのでまた精度を上げて再挑戦したいです。