Pythonでいろいろやってみる

Pythonを使った画像処理や機械学習などの簡単なプログラムを載せています。

図形をランダムな色で塗りつぶす

輪郭で描かれている画像を自動で塗りつぶします。OpenCVの領域検出で図形を検出し、検出された領域の座標を使ってOpenCVのfillPolyで色を塗ります。

関連記事

回転を考慮した外接矩形を描画する

使った関数・メソッド
  • cv2.imread : 画像ファイルの読み出し
  • cv2.threshold : 画像の二値化
  • cv2.findContours : 領域検出
  • cv2.fillPoly : 多角形による塗りつぶし
  • cv2.imwrite : 画像をファイルに保存
環境
  • windows10 home
  • jupyter notebook 6.3.0
  • Python 3.8.8
  • OpenCV 4.0.1
準備

次の画像ファイル'figs.jpg'をjupyter notebookファイルと同じディレクトリにコピーしています。
f:id:T_A_T:20210819210612j:plain

コード

検出された領域はcontoursに収められますが、その一番初めの要素contours[0]には画像の外周が検出されてその領域が収められています。それを除くためcontours[1]以降の領域を取り出し、それぞれの領域をランダムな色で塗りつぶす処理をしています。

import cv2
import numpy as np
import random

colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255)]

img = cv2.imread('figs.jpg') 
img_gray = cv2.imread('figs.jpg', cv2.IMREAD_GRAYSCALE)  

# 閾値126 でグレイスケール画像を二値化
ret, img_binary= cv2.threshold(img_gray, 126, 255, cv2.THRESH_BINARY)  
# 領域検出
contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 

# 検出した領域を一つずつ取り出しランダムな色で塗りつぶす
# contours[0]は画像全体なのでcontours[1]以降を用いる
for j in contours[1:]:
    cv2.fillPoly(img, [j], random.choice(colors), cv2.LINE_AA)

cv2.imwrite('draw.jpg', img)
実行結果

図形がランダムな色で塗りつぶされました。
f:id:T_A_T:20210819211052j:plain
それぞれの図形が二重に塗りつぶされていますが、これはそれぞれの図形が輪郭の外側と輪郭の内側で2回領域検出されて2回ずつ塗りつぶされているためです。

hierarchyは領域の親子関係を示すもので、これを使うことで一番内側の領域のみ塗りつぶすことが可能です。hierarchyを見ると

array([[[-1, -1, 1, -1],
[ 3, -1, 2, 0],
[-1, -1, -1, 1],
[ 5, 1, 4, 0],
[-1, -1, -1, 3],
[-1, 3, 6, 0],
[-1, -1, -1, 5]]], dtype=int32)

となっており、各行が検出された領域に対応します(ここでは7個)。各行の4つの要素は[次のインデックス、前のインデックス、最初の子のインデックス、親のインデックス] を示しています。-1は該当するインデックスが無いことを意味します。一番内側の領域は「最初の子のインデックス」が-1となるため、この場合のみ色を塗れば輪郭の内側の領域のみ塗りつぶすことができます。

コード
import cv2
import numpy as np
import random

colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255)]

img = cv2.imread('figs.jpg') 
img_gray = cv2.imread('figs.jpg',cv2.IMREAD_GRAYSCALE)  

# 閾値126 でグレイスケール画像を二値化
ret, img_binary= cv2.threshold(img_gray, 126, 255, cv2.THRESH_BINARY)  
# 領域検出
contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 

# hierarchyの最初の子要素がない領域のみランダムな色で塗りつぶす
for j, k in zip(contours, hierarchy[0]):
    if k[2] == -1:
        cv2.fillPoly(img, [j], random.choice(colors), cv2.LINE_AA)

cv2.imwrite('draw.jpg', img)
実行結果

内側の領域のみ塗りつぶされました。
f:id:T_A_T:20210824180802j:plain

以下のサイトを参考にさせていただきました

OpenCV-Python Tutorials 1 documentation >> 領域(輪郭)の特徴
Pystyle >> OpenCV – findContours で画像から輪郭を抽出する方法

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村