#1.はじめに
Python+OpenCVでイラストを書いてみました。
絵心がなく絵が描けない→プログラムは書ける→プログラムで絵を描けばいいじゃない という無謀な発想です。
線の位置を全部ベタで座標指定していて難しいことは全くしていません。
#2.実行結果
#3.スクリプト全体
girl1.py
import numpy as np
import cv2
# 白で塗りつぶす
img = np.full((600, 800, 3), 255, dtype=np.uint8)
# 補助線
#cv2.rectangle(img, (300, 300), (500, 500), (127, 127, 127), 1, cv2.LINE_AA)
#cv2.line( img, (300, 400), (500, 400), (127, 127, 127), 1, cv2.LINE_AA)
#cv2.line( img, (400, 300), (400, 500), (127, 127, 127), 1, cv2.LINE_AA)
#cv2.line( img, (300, 450), (500, 450), (127, 127, 127), 1, cv2.LINE_AA)
#cv2.line( img, (300, 435), (500, 435), (127, 127, 127), 1, cv2.LINE_AA)
#cv2.circle( img, (400, 400), 100, (127, 127, 127), 1, cv2.LINE_AA)
#cv2.ellipse( img, ((400, 400), (170, 200), 0), (127, 127, 127), 1, cv2.LINE_AA)
# 髪塗りつぶし 全部繋げるとうまく塗りつぶせないので4つに分けている
pts1_1 = np.array([
# 髪(右外側)
(400, 270),
(415, 271),
(430, 273),
(440, 276),
(450, 280),
(460, 285),
(470, 290),
(480, 298),
(490, 309),
(495, 315),
(500, 330),
(504, 340),
(507, 350),
(509, 360),
(510, 370),
(510, 400),
(509, 430),
(507, 460),
(504, 500),
# 首(右)
(424, 500),
(425, 491),
# 輪郭右(耳除く)
(430, 489),
(440, 485),
(450, 480),
(460, 470),
(465, 458),
(467, 450),
(475, 447),
(478, 440),
(480, 434),
(475, 430),
(479, 420),
(482, 410),
(485, 400),
(488, 390),
# 髪(右内側)途中まで
(488, 390),
(480, 385),
(456, 378),
(452, 350),
(448, 330),
], dtype=np.int32)
cv2.fillConvexPoly(img, pts1_1, (127, 127, 127))
# 髪塗りつぶし
pts1_2 = np.array([
# 頭頂
(400, 270),
# 髪(右内側)途中から
(440, 315),
(444, 330),
(448, 350),
(450, 376),
(430, 372),
(405, 370),
(404, 350),
(403, 325),
(402, 320),
(401, 310),
(400, 305),
], dtype=np.int32)
cv2.fillConvexPoly(img, pts1_2, (127, 127, 127))
# 髪塗りつぶし
pts1_3 = np.array([
# 頭頂
(400, 270),
# 髪(左内側)途中まで
# (400, 305),
(399, 310),
(398, 320),
(397, 325),
(396, 350),
(395, 370),
(370, 372),
(350, 376),
(352, 350),
(356, 330),
(360, 315),
], dtype=np.int32)
cv2.fillConvexPoly(img, pts1_3, (127, 127, 127))
# 髪塗りつぶし
pts1_4 = np.array([
# 髪(左内側)途中から
(352, 330),
(348, 350),
(344, 378),
(320, 385),
(312, 390),
# 輪郭左(耳除く)
(312, 390),
(315, 400),
(318, 410),
(321, 420),
(325, 430),
(320, 434),
(322, 440),
(325, 447),
(333, 450),
(335, 458),
(340, 470),
(350, 480),
(360, 485),
(370, 489),
# 首(左)
(375, 491),
(376, 500),
# 髪(左外側)
(296, 500),
(293, 460),
(291, 430),
(290, 400),
(290, 370),
(291, 360),
(293, 350),
(296, 340),
(300, 330),
(305, 315),
(310, 309),
(320, 298),
(330, 290),
(340, 285),
(350, 280),
(360, 276),
(370, 273),
(385, 271),
(400, 270),
], dtype=np.int32)
cv2.fillConvexPoly(img, pts1_4, (127, 127, 127))
# 輪郭
pts2 = np.array([
# (400, 300),
# (410, 301),
# (420, 303),
# (430, 306),
# (440, 310),
# (450, 315),
# (460, 320),
# (470, 328),
# (480, 339),
# (485, 345),
# (491, 360),
# (491, 370),
# (490, 380),
(488, 390),
(485, 400),
(482, 410),
(479, 420),
(475, 430),
(470, 440),
(465, 458),
(460, 470),
(450, 480),
(440, 485),
(430, 489),
(420, 493),
(410, 497),
(400, 500),
(390, 497),
(380, 493),
(370, 489),
(360, 485),
(350, 480),
(340, 470),
(335, 458),
(330, 440),
(325, 430),
(321, 420),
(318, 410),
(315, 400),
(312, 390),
# (310, 380),
# (309, 370),
# (309, 360),
# (315, 345),
# (320, 339),
# (330, 328),
# (340, 320),
# (350, 315),
# (360, 310),
# (370, 306),
# (380, 303),
# (390, 301),
# (400, 300),
], dtype=np.int32)
cv2.polylines(img, [pts2], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 右目
cv2.ellipse( img, (440, 430), ( 12, 20), 180, 0, 180, (127, 127, 127), -1, cv2.LINE_AA)
cv2.ellipse( img, ((440, 430), ( 25, 40), 0), ( 0, 0, 0), 2, cv2.LINE_AA)
cv2.ellipse( img, ((440, 430), ( 10, 16), 0), ( 0, 0, 0), -1, cv2.LINE_AA)
cv2.ellipse( img, ((444, 420), ( 6, 6), 0), (255, 255, 255), -1, cv2.LINE_AA)
cv2.ellipse( img, ((444, 420), ( 6, 6), 0), ( 0, 0, 0), 1, cv2.LINE_AA)
# 左目
cv2.ellipse( img, (360, 430), ( 12, 20), 180, 0, 180, (127, 127, 127), -1, cv2.LINE_AA)
cv2.ellipse( img, ((360, 430), ( 25, 40), 0), ( 0, 0, 0), 2, cv2.LINE_AA)
cv2.ellipse( img, ((360, 430), ( 10, 16), 0), ( 0, 0, 0), -1, cv2.LINE_AA)
cv2.ellipse( img, ((364, 420), ( 6, 6), 0), (255, 255, 255), -1, cv2.LINE_AA)
cv2.ellipse( img, ((364, 420), ( 6, 6), 0), ( 0, 0, 0), 1, cv2.LINE_AA)
# 右眉毛
pts3 = np.array([
(420, 400),
(430, 390),
(440, 385),
(450, 390),
(460, 400),
(470, 415),
], dtype=np.int32)
cv2.polylines(img, [pts3], False, ( 0, 0, 0), 2, cv2.LINE_AA)
#左眉毛
pts4 = np.array([
(380, 400),
(370, 390),
(360, 385),
(350, 390),
(340, 400),
(330, 415),
], dtype=np.int32)
cv2.polylines(img, [pts4], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 右まつ毛
pts5 = np.array([
(425, 418),
(430, 413),
(440, 408),
(450, 413),
(455, 423),
(460, 434),
], dtype=np.int32)
cv2.polylines(img, [pts5], False, ( 0, 0, 0), 2, cv2.LINE_AA)
pts5_5 = np.array([
(460, 434),
(458, 437),
], dtype=np.int32)
cv2.polylines(img, [pts5_5], False, ( 0, 0, 0), 1, cv2.LINE_AA)
# 左まつ毛
pts6 = np.array([
(375, 418),
(370, 413),
(360, 408),
(350, 413),
(345, 423),
(340, 434),
], dtype=np.int32)
cv2.polylines(img, [pts6], False, ( 0, 0, 0), 2, cv2.LINE_AA)
pts6_5 = np.array([
(340, 434),
(342, 437),
], dtype=np.int32)
cv2.polylines(img, [pts6_5], False, ( 0, 0, 0), 1, cv2.LINE_AA)
# 鼻
pts7 = np.array([
(401, 448),
(399, 450),
(401, 452),
], dtype=np.int32)
cv2.polylines(img, [pts7], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 口
pts8 = np.array([
(380, 470),
(385, 473),
(390, 474),
(400, 475),
(410, 474),
(415, 473),
(420, 470),
], dtype=np.int32)
cv2.polylines(img, [pts8], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 首(右)
pts9 = np.array([
(425, 491),
(424, 500),
], dtype=np.int32)
cv2.polylines(img, [pts9], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 首(左)
pts10 = np.array([
(375, 491),
(376, 500),
], dtype=np.int32)
cv2.polylines(img, [pts10], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 右耳
pts11 = np.array([
(475, 430),
(480, 434),
(478, 440),
(475, 447),
(467, 450),
], dtype=np.int32)
cv2.polylines(img, [pts11], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 左耳
pts11 = np.array([
(325, 430),
(320, 434),
(322, 440),
(325, 447),
(333, 450),
], dtype=np.int32)
cv2.polylines(img, [pts11], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 髪(右外側)
pts12 = np.array([
(400, 270),
(415, 271),
(430, 273),
(440, 276),
(450, 280),
(460, 285),
(470, 290),
(480, 298),
(490, 309),
(495, 315),
(500, 330),
(504, 340),
(507, 350),
(509, 360),
(510, 370),
(510, 400),
(509, 430),
(507, 460),
(504, 500),
], dtype=np.int32)
cv2.polylines(img, [pts12], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 髪(左外側)
pts13 = np.array([
(400, 270),
(385, 271),
(370, 273),
(360, 276),
(350, 280),
(340, 285),
(330, 290),
(320, 298),
(310, 309),
(305, 315),
(300, 330),
(296, 340),
(293, 350),
(291, 360),
(290, 370),
(290, 400),
(291, 430),
(293, 460),
(296, 500),
], dtype=np.int32)
cv2.polylines(img, [pts13], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 髪(右内側)
pts16 = np.array([
(400, 305),
(401, 310),
(402, 320),
(403, 325),
(404, 350),
(405, 370),
(430, 372),
(450, 376),
(448, 350),
(444, 330),
(440, 315),
(448, 330),
(452, 350),
(456, 378),
(480, 385),
(488, 390),
], dtype=np.int32)
cv2.polylines(img, [pts16], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 髪(左内側)
pts17 = np.array([
(400, 305),
(399, 310),
(398, 320),
(397, 325),
(396, 350),
(395, 370),
(370, 372),
(350, 376),
(352, 350),
(356, 330),
(360, 315),
(352, 330),
(348, 350),
(344, 378),
(320, 385),
(312, 390),
], dtype=np.int32)
cv2.polylines(img, [pts17], False, ( 0, 0, 0), 2, cv2.LINE_AA)
# 右頬
cv2.line(img, (430, 460), (435, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (434, 460), (439, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (438, 460), (443, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (442, 460), (447, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (446, 460), (451, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
# 左頬
cv2.line(img, (350, 460), (355, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (354, 460), (359, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (358, 460), (363, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (362, 460), (367, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.line(img, (366, 460), (371, 455), ( 0, 0, 0), 1, cv2.LINE_AA)
cv2.imshow('Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#4.参考
Python, OpenCVで図形描画(線、長方形、円、矢印、文字など) | note.nkmk.me
[Amazon.co.jp: 萌えキャラクターの描き方 顔・からだ編 (マンガの技法書) eBook: 伊原達矢, 角丸つぶら: Kindleストア]
(https://www.amazon.co.jp/dp/B014KS0WGY)
#5.その他
OpenCVだと曲線は円弧(楕円含む)しかなく、曲線を書きづらいので、続きはGIMP Python-Fuに移行しています。
→GIMP Python-Fuでイラストを描く その1 - Qiita