図形をランダムな色で塗りつぶす
輪郭で描かれている画像を自動で塗りつぶします。OpenCVの領域検出で図形を検出し、検出された領域の座標を使ってOpenCVのfillPolyで色を塗ります。
関連記事
使った関数・メソッド
- cv2.imread : 画像ファイルの読み出し
- cv2.threshold : 画像の二値化
- cv2.findContours : 領域検出
- cv2.fillPoly : 多角形による塗りつぶし
- cv2.imwrite : 画像をファイルに保存
環境
準備
次の画像ファイル'figs.jpg'をjupyter notebookファイルと同じディレクトリにコピーしています。
コード
検出された領域は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)
実行結果
図形がランダムな色で塗りつぶされました。
それぞれの図形が二重に塗りつぶされていますが、これはそれぞれの図形が輪郭の外側と輪郭の内側で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)
実行結果
内側の領域のみ塗りつぶされました。
以下のサイトを参考にさせていただきました
OpenCV-Python Tutorials 1 documentation >> 領域(輪郭)の特徴
Pystyle >> OpenCV – findContours で画像から輪郭を抽出する方法