10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PythonAdvent Calendar 2022

Day 5

ezdxfとopenCVで画像認識してCAD化する(ezdxfシリーズ番外編)

Last updated at Posted at 2022-12-12

ezdxfとopenCVで画像認識してCAD化する

写真を画像認識してCAD化する業務を副業で納品しました。
とても勉強になりましたので、趣味と実益を兼ねてまとめてみます。

ezdxfはPythonでCADデータを編集・作成するライブラリ、
openCVはPythonのお馴染み画像編集・認識ライブラリです。

今回はこの2つのライブラリを連携して遊んでみたいと思います。

画像認識の題材

折角なら格好いいものをCAD化したいので、私の好きなゲーム『Guilty Gear』シリーズの武器『ジャンクヤードドッグMK-Ⅲ』に挑戦します。
pekka-hyvarinen-1.jpg
『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」とした上で、その間の値を指定します。
↓のようなグラデーションをイメージしてください
グレースケール.jpg

下記関数は、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です。
リストを使ったポリラインの描画についてこちらを参照ください。

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()

抽出結果とまとめ

jkd.png
細かい調整については省きますが、『ジャンクヤードドッグ』の輪郭線がポリラインで抽出できました。恰好いいですね~。

フリー素材CADデータの人物も、たちまちバッドガイです。

image.png

※注意:
参考画像とCADデータが上下反転して出力されています。
これはopencvとCADの座標系の違いです。
y座標が大きくなると上にいくか下に行くかという違いです。
本筋からずれるので処理しておりません。ご了承ください。

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?