はじめに
今回の記事は以下の記事からの続きです。
クラウド開発環境PaizaCloudクラウドIDEでHello World(OpenCV編 その1)
前回の記事ではOpenCVの「前処理」の例を試してみました。今回のその続きの「解析」にあたる処理を試してみます。
今回の前提について
前回と同じですが、本記事では画像処理の理論そのものは扱いません。(※というか私ではうまく説明出来ません。)あくまで「入力 → ライブラリで処理 → 出力」という流れを体験することを目的にしています。
今回のプログラム
以下が今回のプログラムです。画像内の多角形を解析します。
detect_shapes.py
import cv2
# 1) 画像の読み込み
img = cv2.imread("shapes_sample.png") # ← ここに同名で置く
if img is None:
raise FileNotFoundError("shapes_sample.png が見つかりません")
# 2) グレースケール化(前処理)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite("step1_gray.png", gray)
# 3) 二値化(前処理)
# 彩色図形(赤/青/緑)はグレーにすると 30~150 程度の濃度になります。
# 図形を白(=前景)、背景を黒にしたいので BINARY_INV を使います。
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
cv2.imwrite("step2_binary.png", binary)
# 4) 輪郭抽出(解析)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 5) 近似で頂点数を数える(解析)
out_contours = img.copy()
out_labeled = img.copy()
def name_by_vertices(v):
if v == 3: return "Triangle"
if v == 4: return "Quadrilateral"
if v == 5: return "Pentagon"
if v == 6: return "Hexagon"
# 7以上はだいたい円っぽいものとして
return f"{v}-gon"
for cnt in contours:
# 輪郭を簡略化(頂点を減らして近似): 0.02*周長 が定番の初期値
epsilon = 0.02 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
vertices = len(approx)
# 輪郭の描画(緑)
cv2.drawContours(out_contours, [cnt], -1, (0,255,0), 2)
# ラベル描画用座標
x, y, w, h = cv2.boundingRect(approx)
label = name_by_vertices(vertices)
# ラベルを重ねる(赤文字)
cv2.putText(out_labeled, label, (x, y-8),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 2, cv2.LINE_AA)
cv2.drawContours(out_labeled, [approx], -1, (0,255,0), 2)
# 6) 途中経過と結果の保存
cv2.imwrite("step3_contours.png", out_contours) # 輪郭だけ描いた画像
cv2.imwrite("step4_labeled.png", out_labeled) # ラベル付き完成画像
print(f"検出した輪郭数: {len(contours)}")
print("保存しました: step1_gray.png, step2_binary.png, step3_contours.png, step4_labeled.png")
期待される判定(このサンプル画像)
- 赤い三角形 → Triangle (3頂点)
- 青い四角形 → Quadrilateral (4頂点)
- 緑の五角形 → Pentagon (5頂点)
何が起きてるか(見る順番)
- step1_gray.png
カラーをグレーに。赤=76、青=29、緑=150、背景は255(白)くらいの濃度になります。
- step2_binary.png
THRESH_BINARY_INV で 図形が白、背景が黒 に。輪郭抽出の前処理として扱いやすい形。
- step3_contours.png
抽出した輪郭を緑で重ね描画。検出個数がコンソールにも出ます。(緑は重なっています)
- step4_labeled.png
approxPolyDP の頂点数で Triangle / Quadrilateral / Pentagon をラベリング。
うまくいかない時の調整ポイント
- 図形が欠ける/ギザギザ
- 二値化を cv2.threshold(gray, しきい値, 255, cv2.THRESH_BINARY_INV) の しきい値 を 100〜160 で微調整
- 事前に cv2.GaussianBlur(gray, (3,3), 0) で軽くノイズを抑えると安定
- 頂点数が多すぎる/少なすぎる
- epsilon = 0.02 * 周長 を 0.01〜0.03 に調整(数値を大きくすると頂点が減り、小さくすると増える)
- 余計な小さなゴミ輪郭が混じる
- cv2.contourArea(cnt) で面積が小さい輪郭をスキップ(例:面積 < 100 なら continue)
実行の手順(GUIなし環境)
shapes_sample.png を作業ディレクトリへ
上記のPythonプログラムをdetect_shapes.pyで画像ファイルと同じで作業ディレクトリに保存
ターミナルで実行
$ python deltect_shapes.py
PaizaCloudでの実行例
輪郭(図形)を3つ認識して、出力ファイルが生成されています。
さいごに
今回はここまでです。前回と合わせて、OpenCVの前処理から解析の流れを簡単ではありますが試してみました。引き続きOpenCVも含めていろいろと投稿出来ればと思います。
ありがとうございました。