Pythonで画像の輪郭を抽出してdxfとして出力する

こんてんつ

画像内のフチを抽出してCAD(.dxf)ファイルとして出力する方法をまとめる。

  • 結論(完成系)
  • コード(画像のアップロードと表示)
  • コード(輪郭抽出とmerge)
  • コード(.dxf出力)

結論(完成系)

例題

下記の画像から輪郭を抽出する。

f:id:norunblog:20211219235012j:plain

コード

Google Colaboratoryを利用して実装する。

import cv2
import numpy as np
from google.colab import files
from matplotlib import pyplot as plt

### 画像のアップロードと表示

uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
plt.axis('off')
plt.imshow(src)

### 輪郭を作成して元の画像にmerge

frame = cv2.imread(uploaded_file_name)
canny = cv2.Canny(frame, 255, 0)
contours, hierarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours_draw = cv2.drawContours(frame, contours, -1, (0, 255, 0), 1)

merged_file_name = "merged.jpg"
cv2.imwrite(merged_file_name, contours_draw)

orig = cv2.imread(merged_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
plt.axis('off')
plt.imshow(src)

### 輪郭部分のみを取り出してdxfファイルを出力する

import ezdxf
dwg = ezdxf.new("R2010")
msp = dwg.modelspace()
dwg.layers.new(name="greeny green lines", dxfattribs={"color": 3})

squeezed = [np.squeeze(cnt, axis=1) for cnt in contours]
inverted_squeezed = [arr * [1, -1] for arr in squeezed]

for ctr in inverted_squeezed:
     for n in range(len(ctr)):
         if n >= len(ctr) - 1:
             n = 0
         try:
             msp.add_line(ctr[n], ctr[n + 1], dxfattribs={"layer": "greeny green lines", "lineweight": 20})
         except IndexError:
             pass

dwg.saveas('output.dxf')

コードを少し解説

画像のアップロードと表示

import cv2
import numpy as np
from google.colab import files
from matplotlib import pyplot as plt

uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
plt.axis('off')
plt.imshow(src)

プログラムが途中で止まって、アップロードする画像のファイルを選択するよう指示される。 f:id:norunblog:20211219232700p:plain

画像をアップロードしたら、次のように表示される。 f:id:norunblog:20211219232831p:plain

輪郭抽出とmerge

import cv2
import numpy as np
from google.colab import files
from matplotlib import pyplot as plt

### 画像のアップロード

uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))

### 輪郭の作成

frame = cv2.imread(uploaded_file_name)
canny = cv2.Canny(frame, 255, 0)
contours, hierarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours_draw = cv2.drawContours(frame, contours, -1, (0, 255, 0), 1)

### merge

merged_file_name = "merged.jpg"
cv2.imwrite(merged_file_name, contours_draw)

### 画像の表示

orig = cv2.imread(merged_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
plt.axis('off')
plt.imshow(src)

f:id:norunblog:20211219233259p:plain

merged.jpgを確認すると、輪郭が緑線で付けられていることが分かる。

f:id:norunblog:20211219233609p:plain

CAD(.dxf)出力

import cv2
import numpy as np
from google.colab import files
from matplotlib import pyplot as plt

### 画像のアップロード

uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))

### 輪郭の作成

frame = cv2.imread(uploaded_file_name)
canny = cv2.Canny(frame, 255, 0)
contours, hierarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours_draw = cv2.drawContours(frame, contours, -1, (0, 255, 0), 1)

### 輪郭部分のみを取り出してdxfファイルを出力する

import ezdxf
dwg = ezdxf.new("R2010")
msp = dwg.modelspace()
dwg.layers.new(name="greeny green lines", dxfattribs={"color": 3})

squeezed = [np.squeeze(cnt, axis=1) for cnt in contours]
inverted_squeezed = [arr * [1, -1] for arr in squeezed]

for ctr in inverted_squeezed:
     for n in range(len(ctr)):
         if n >= len(ctr) - 1:
             n = 0
         try:
             msp.add_line(ctr[n], ctr[n + 1], dxfattribs={"layer": "greeny green lines", "lineweight": 20})
         except IndexError:
             pass

dwg.saveas('output.dxf')

output.dxfというCADファイルが作成されている。

f:id:norunblog:20211219234104p:plain