こんにちは、Yuiです。
引き続き週イチ発信をしていきます。
今週はボタン一つで漫画風の画像にできるサービスを作ったのでその件について書きます。
ボタン一つで画像を漫画風にできるアプリを作りました!
— yui 🌤️ Yuiko Ito (@yui_active) July 18, 2021
エフェクトも色々試せるのでぜひ遊んでみてください!#漫画ツクールhttps://t.co/BGXrp64Zvd pic.twitter.com/3ZIElKVMz5
過去の週イチ発信は以下
- 【React + Typescriptで顔認識】tensorflowを使って画像にマスクをかけるアプリを作った
- 【React + Typescript】ボタン一つでコンポーネントのscssをコピーできるサイトを作った
- 【アップデート】ui-componentsに18個のコンポーネントを追加した
- 【Nuxt.js × Tailwind CSS】ボタン一つで有名絵画風の画像にできるサービスをリリースした!
- 【GASでLINE Bot作成】現在地の近くのおすすめのごはん屋さんを教えてくれるLINE Botを作った
- 【動的OGP】Next.js + TypeScript + Vercelデプロイで動的OGPを実現する
- 【LambdaでOpenCVを利用】AWSとOpenCVを利用してポケモン画像でアスキーアート風に変換するAPIを作った
- ポケモン画像でアスキーアート風に変換するwebアプリを作った
使い方
使い方はとても簡単です。
まずは上記URLにアクセスして、画像を選択します。
私が力尽きてトリミング機能の実装をサボったのでエフェクトを合わせる場合は横長の画像の方がうまくいきます。(大体正方形〜2:1ぐらいのサイズなら違和感ありませんでした。)
これはエフェクトの画像サイズを1280px×840pxにしているのですが、入力画像に合わせて伸びるようにしているため、縦長の画像の場合は文字が縦に細長くなってしまうからです。
画像を選択したら、エフェクト類を自由に選択できます。
エフェクトは人物の背景に入れるものと、画像の上に重ねるものの両方をそれぞれ選択できます。
デフォルトでは何もエフェクトをつけない状態のものを選択しているので、必要に応じて変更してください。
なお、背景にエフェクトを入れる場合は、できるだけ人物と背景の境界線がはっきりしているものを選ばないとうまく探知できないと思います。これは今回機械学習で人物探知をしているのではなく、輪郭抽出をして背景を入れ替える実装にしているからです。
エフェクトを選んだら変換ボタンをクリックして数秒待てば変換されます。
今回機械学習を使ってないので、かなり早く変換されると思います。
ちなみに今回サンプルとして利用させてもらったおじさんは以下から取得させてもらいました。
個人的にお気に入りのフリー素材です。
構成
以下のおなじみの構成です。
フロント...Nuxt.js+tailwind css
バックエンド...Python
API化...AWS
ホスティング...Vercel
これまでリリースした〇〇ツクール系のアプリはほとんど同じ構成で組んでいます。
API化などは【LambdaでOpenCVを利用】AWSとOpenCVを利用してポケモン画像でアスキーアート風に変換するAPIを作ったで詳しく書いたので、記事通りやれば作れると思います。
【これまで開発したツクール系アプリたち】
塗り絵ツクール
絵画ツクール
ポケモンAAツクール
工夫したところ
今回、漫画化の部分は多少閾値を調整した以外は以下のYoutubeそのままなので、興味のある方は動画を参考にしてください。
こちらで工夫したところとしては、エフェクトを追加する部分です。
まず、背景エフェクトをつける部分に関しては、輪郭を抽出して切り抜いてから画像と合成するという処理をとっています。
# 背景エフェクトをつける
def back_filter(src, manga, effect, th):
# グレースケール変換
img = cv2.bitwise_not(src)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
effect = cv2.cvtColor(effect, cv2.COLOR_BGR2GRAY)
# スクリーントーン画像を入力画像と同じ大きさにリサイズ
effect = cv2.resize(effect,(img_gray.shape[1],img_gray.shape[0]))
# 2値化
ret, img_binary = cv2.threshold(img_gray, th, 255,cv2.THRESH_BINARY)
# 輪郭抽出する。
contours, _ = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 面積が最大の輪郭を取得する
contour = max(contours, key=lambda x: cv2.contourArea(x))
mask = np.zeros_like(img_binary)
cv2.drawContours(mask, [contour], -1, color=255, thickness=-1)
effect = np.where(mask == 0, effect, manga)
# 三値画像と輪郭画像を合成
return effect
画像の上にかけるエフェクトに関してはもっとシンプルで、単に合成してるだけです。
# フロントエフェクト
def front_filter(manga, effect):
effect = cv2.resize(effect,(manga.shape[1], manga.shape[0]))
#マスク画像作成
mask = effect[:,:,3]
# エフェクトをグレースケール化
effect = cv2.cvtColor(effect, cv2.COLOR_BGR2GRAY)
# 三値画像と輪郭画像を合成
manga = np.where(mask == 0, manga, effect)
return manga
上記の構成のため、背景エフェクトはjpgで、画像の上に重ねるエフェクトはpngの背景透過画像を利用しています。
あとがき
今回はほとんど前に作ったツクール系アプリと同じ構成だったので、だいたい半日ぐらいで作れました。
エフェクト画像に関しては、商業利用OKのフリー素材をかきあつめてcanvaで加工して作りました。
他にこんなエフェクトがほしいなどリクエストあれば受け付けております!
それでは最後までお読みいただきありがとうございました!
もし少しでも参考になった、面白いと思ってもらえたらLGTMお願いいたします!
参考
https://youtu.be/FSwKUNp2U5U
https://assets.clip-studio.com/ja-jp/detail?id=1735354
http://www.manga-sozai.com/data/category/line/page/2
https://dddfont.com/
https://irasutomantul.blogspot.com/2020/06/blog-post_61.html#halaman2
https://mangasozai.com/