たまには、猫画像でちょっと遊んでみようかと 仕事でもプログラムは書いていますが、たまに「何の役にも立たないけど楽しいこと」をやってみたくなり、、、 今日はそんな気分で、猫の画像を使ってちょっとした画像アートのようなことをしてみたので、記録として残してみようと思います。
技術的にはPythonとOpenCVを使っていますが、文系出身の私でも感覚的に楽しめる内容なので、ぜひお気軽に読んでいただけたらと思います。。🐾
(この記事は広告を含みます)
- 1. 目指す完成形
- 2. 猫画像をPythonで読み込む(癒しから始めましょう)
- 3. 使用するライブラリ
- 4. やったことのざっくり手順
- 5. 試行錯誤のポイント
- 6. 最終コード(コピペOK)
- 7. 最後に:アートは試行錯誤が楽しい
1. 目指す完成形
最終的にはこんな仕上がりにしたい!というイメージ👇
- 元画像を残しつつ、アートっぽく三角形で分割
- お目目キラキラ、鼻ピンクふんわり
- 隙間やカクカク感がなく、全体がなめらか
2. 猫画像をPythonで読み込む(癒しから始めましょう)
まずは猫画像を用意して、それをPythonで読み込んでみます。
癒される画像じゃないと、そもそもやる気が出ませんからね。。
OpenCVはコチラの記事を参考に
3. 使用するライブラリ
pip install opencv-python numpy matplotlib
4. やったことのざっくり手順
1. 猫画像を読み込む 2. 特徴点を抽出(目・鼻を重視) 3. ドロネー三角形分割を行う 4. 各三角形ごとに平均色で塗る 5. 明るさや目の中をちょこっと補正 6. 最後にふんわりブラーをかけて完成!
5. 試行錯誤のポイント
特徴点を大量に拾う「ハイポリ化」
- goodFeaturesToTrack() で点を2000点以上取得
- qualityLevel をかなり下げて細かい部分まで抽出
お目目を守る「フォーカス点」
- 目や鼻など、表情に関わるパーツは手動で補完
- 絶妙な位置に点を追加することで、自然な印象に
塗り色をちょこっと明るく補正
- 三角形ごとの平均色が暗くなりすぎないよう、明度を少しだけ上げる
キャンバス初期化は「白」
- OpenCVの fillConvexPoly() は境界に隙間ができやすい
- 初期状態を「白」で塗っておくことで黒い隙間問題を回避
最後のひと手間「ブラー処理」
- 三角形の境界をなめらかに見せるため、軽めのガウシアンブラーでふんわり演出
6. 最終コード(コピペOK)
import cv2 import numpy as np import matplotlib.pyplot as plt # === 画像読み込み === img = cv2.imread("IMG_5835.JPG") if img is None: raise FileNotFoundError("画像ファイルが見つかりませんでした。ファイル名やパスを確認してください。") h, w = img.shape[:2] # === 特徴点の検出(ハイポリ)=== gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) points = cv2.goodFeaturesToTrack( gray, maxCorners=2000, qualityLevel=0.0001, minDistance=1 ) points = points.reshape(-1, 2) # === 境界補完点 === border_points = np.array([ [0, 0], [w // 2, 0], [w - 1, 0], [0, h // 2], [w - 1, h // 2], [0, h - 1], [w // 2, h - 1], [w - 1, h - 1] ], dtype=np.float32) # === 顔・目の注目ポイント === face_focus = np.array([ [w // 2 - 50, h // 2 + 20], # 鼻 ], dtype=np.float32) eye_focus = np.array([ # 左目まわり [w // 2 - 60, h // 2 - 45], [w // 2 - 50, h // 2 - 40], [w // 2 - 55, h // 2 - 35], # 右目まわり [w // 2 + 45, h // 2 - 45], [w // 2 + 55, h // 2 - 40], [w // 2 + 50, h // 2 - 35], ], dtype=np.float32) # === 全ての点を統合 === all_points = np.concatenate([points, border_points, face_focus, eye_focus]) # === ドロネー三角形分割 === subdiv = cv2.Subdiv2D((0, 0, w, h)) for p in all_points: subdiv.insert((p[0], p[1])) triangleList = subdiv.getTriangleList() # === キャンバス作成(白背景で初期化)=== canvas = np.full_like(img, fill_value=255) # === 各三角形を平均色で塗る(補正・ノイズ除去あり)=== for t in triangleList: pts = t.reshape(3, 2).astype(np.int32) if np.any(pts < 0) or np.any(pts[:, 0] > w) or np.any(pts[:, 1] > h): continue area = cv2.contourArea(pts) if area < 5: continue mask = np.zeros(img.shape[:2], dtype=np.uint8) cv2.fillConvexPoly(mask, pts, 255) mean_color = cv2.mean(img, mask=mask)[:3] brightness = np.mean(mean_color) # 明るさ補正(暗ければ強めに補正) if brightness < 60: bright_color = tuple(min(255, int(c * 1.15)) for c in mean_color) else: bright_color = tuple(min(255, int(c * 1.05)) for c in mean_color) cv2.fillConvexPoly(canvas, pts, bright_color) # === 最終仕上げ:スムース処理でふんわり演出 === canvas_rgb = cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB) smoothed = cv2.GaussianBlur(canvas_rgb, (3, 3), sigmaX=0.5) # === 表示 === img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.figure(figsize=(10, 5)) plt.subplot(1, 2, 1) plt.imshow(img_rgb) plt.axis("off") plt.title("original") plt.subplot(1, 2, 2) plt.imshow(smoothed) plt.axis("off") plt.title("triangulated (final ver.)") plt.tight_layout() plt.show()
7. 最後に:アートは試行錯誤が楽しい
最初は「猫画像をちょっと遊んでみよう」くらいの気持ちでしたが、 やってみると、
- 三角形の数や大きさで雰囲気が全然違う
- 目の周りは繊細に作らないと可愛さが出ない
- ブラーや補色のちょっとした工夫がすごく大事
などなど、ちょっとした数値の違いで印象が大きく変わる世界でした。 自動化は無理だなぁと思い、こういうのこそ、AIさんに任せるのがいいのかな?
でも、画像処理の勉強になりました。