ezdxfとopenCVで画像認識してCAD化する
写真を画像認識してCAD化する業務を副業で納品しました。
とても勉強になりましたので、趣味と実益を兼ねてまとめてみます。
ezdxfはPythonでCADデータを編集・作成するライブラリ、
openCVはPythonのお馴染み画像編集・認識ライブラリです。
今回はこの2つのライブラリを連携して遊んでみたいと思います。
画像認識の題材
折角なら格好いいものをCAD化したいので、私の好きなゲーム『Guilty Gear』シリーズの武器『ジャンクヤードドッグMK-Ⅲ』に挑戦します。
『ezdxfを使ってみよう』シリーズは格ゲー勢に優しいプログラミング記事です。
目次
1.ライブラリのインポート
2.openCVで画像の輪郭を抽出する関数の作成
3.画像を読み込み、上記2の関数を呼び出す
4.ezdxfで輪郭線を描画する
ライブラリのインポート
import cv2
from matplotlib import pyplot as plt
import numpy as np
import ezdxf
openCVで画像の輪郭を抽出する関数の作成
この項目では以下の作業をプログラムが行います。
- 画像をグレースケール化して読み込む
- 指定した明暗の境界(グレースケールの色番号の境界)の境界の座標を取得する
指定した明暗の境界は0~255で指定できます。
真っ黒を「0」、真っ白を「255」とした上で、その間の値を指定します。
↓のようなグラデーションをイメージしてください
下記関数は、0~256の間の整数が格納された変数lightを引数として動きます
def line_make(light):
# 画像の白黒化・明暗の境界を指定
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
ret, img_binary = cv2.threshold(img_gray,
light, 255, #グレースケールlight~255の領域を抽出の意
cv2.THRESH_BINARY)
# 輪郭抽出
contours, hierarchy = cv2.findContours(img_binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
# 輪郭を元画像に描画
img_contour = cv2.drawContours(img, contours, -1, (0, 255, 0), 5)
#抽出した輪郭の座標の抽出・リスト化
contours_list = []
for i in contours:
if len(i) > 1 :
lista = [j[0].tolist() for j in i] #座標はarray.numpyの形になっているので、リストにする
contours_list.append(lista)
#輪郭の座標のリストを返す
return contours_list
※注意:opencvは座標値をnp.arrayで返します。np.arrayはezdxfで扱えないので、プログラム内で整数のリストに変換する措置を取っています。
画像を読み込み、関数【line_make()】を呼び出す
先に関数を書いてしまいましたが、プログラムの実質的なスタートはここからです。
抽出する明暗の境界値は複数の方が、期待する結果が得やすいです。
プログラム内のリスト【light_list】を修正してご使用ください。
forループでの使用前提と可読性のために、前項で関数を作成しております。
# 画像の読み込み(cv2.imreadの引数に画像URLを指定)
img = cv2.imread('pekka-hyvarinen-1.jpg', 1)
#多角形として描画する座標のリストをつくります。
lines_list = []
#上記関数line_make()に指定するlight(明暗抽出の下限値)をリスト化します
light_list = [50,100,150]
#上記関数line_make()で座標リストを取得し、lines_listに順次格納します。
for light in light_list:
lines_list.append(line_make(light))
明暗の境界値別の座標値のリストが【lines_list】に格納されました
ezdxfで輪郭線を描画する
現時点で用意できているものは、『明暗の境界の座標』の点情報のみです。データ内は限りなく線に近い精度で点描されておりますが、点の集合体に過ぎません。
この点の集合体のリストを繋げてのポリラインを作成するのがezdxfです。
リストを使ったポリラインの描画についてこちらを参照ください。
doc = ezdxf.new("R2000", setup=True, units = 4)
msp = doc.modelspace()
for contours_list in lines_list:
for contour_list in contours_list:
poliline_1 = msp.add_lwpolyline(contour_list,
format="xy", close=True, #closeは連続線を閉じるかT/F
dxfattribs={'layer':'mylayer', #レイヤー
'color': 3, #色
'linetype':"Continuous", #線種
'const_width':1 #線幅
})
doc.saveas('junkyarddog.dxf')
#以下matplotlib確認用(不要なら消してください)
from ezdxf.addons.drawing import matplotlib
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ctx = RenderContext(doc)
out = MatplotlibBackend(ax)
Frontend(ctx, out).draw_layout(msp, finalize=True)
fig.show()
抽出結果とまとめ
細かい調整については省きますが、『ジャンクヤードドッグ』の輪郭線がポリラインで抽出できました。恰好いいですね~。
フリー素材CADデータの人物も、たちまちバッドガイです。
※注意:
参考画像とCADデータが上下反転して出力されています。
これはopencvとCADの座標系の違いです。
y座標が大きくなると上にいくか下に行くかという違いです。
本筋からずれるので処理しておりません。ご了承ください。