mirenn所感

お絵かきとプログラミング

Bing Image Search API v7で画像をちょっと保存する方法

f:id:mirenn:20171111205408p:plain
やりたいことがあってけものフレンズの画像を収集することに決めて、それにBing Image Searchを用いることにしました。しかしいざ使おうとすると古いバージョン(v5)がなくてv7しか見当たらずしょうがないからあまり情報がない最新バージョンをやることに。
v5だったら巷にコードがいっぱい落ちていてそのまま使えたんですが、v7は最近も最近だから記事がない。のでv7で、指定した検索ワードで検索された画像を手元に保存するコードを書きました。
 以下全コード。実行環境はpython3.6、windowsです。

# -*- coding: utf-8 -*-
import http.client
import urllib.parse
import requests
import json
import os

# Replace the subscriptionKey string value with your valid subscription key.
subscriptionKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Verify the endpoint URI.  At this writing, only one endpoint is used for Bing
# search APIs.  In the future, regional endpoints may be available.  If you
# encounter unexpected authorization errors, double-check this value against
# the endpoint for your Bing search instance in your Azure dashboard.
host = "api.cognitive.microsoft.com"
path = "/bing/v7.0/images/search"

term = "クラピカ"
save_dir_path = "./hunterxhunter"
count = 0


def make_dir(path):
    if not os.path.isdir(path):
        os.mkdir(path)


def BingImageSearch(search):
    "Performs a Bing image search and returns the results."
    headers = {'Ocp-Apim-Subscription-Key': subscriptionKey}
    conn = http.client.HTTPSConnection(host)
    query = urllib.parse.quote(search)
    conn.request("GET", path + "?q=" + query, headers=headers)
    response = conn.getresponse()
    headers = [k + ": " + v for (k, v) in response.getheaders()
                   if k.startswith("BingAPIs-") or k.startswith("X-MSEdge-")]
    data = response.read()
    conn.close()
    return headers, data.decode("utf8")


def make_img_path(save_dir_path, url, term):
    save_img_path = os.path.join(save_dir_path, term)
    make_dir(save_img_path)
    global count
    count += 1

    file_extension = os.path.splitext(url)[-1]
    if file_extension.lower() in ('.jpg', '.jpeg', '.gif', '.png', '.bmp'):
        full_path = os.path.join(save_img_path, str(count)+file_extension)
        return full_path
    else:
        raise ValueError('Not applicable file extension')


def download_image(url, timeout=10):
    response = requests.get(url, allow_redirects=True, timeout=timeout)
    if response.status_code != 200:
        error = Exception("HTTP status: " + response.status_code)
        raise error

    content_type = response.headers["content-type"]
    if 'image' not in content_type:
        error = Exception("Content-Type: " + content_type)
        raise error

    return response.content


def save_image(filename, image):
    with open(filename, "wb") as fout:
        fout.write(image)


def main():
    if len(subscriptionKey) == 32:
        try:
            make_dir(save_dir_path)
            url_list = []

            print('Searching images for: ', term)
            headers, result = BingImageSearch(term)
        except Exception as err:
            print("[Errno {0}] {1}".format(err.errno, err.strerror))
        else:
            data = json.loads(result)

            for values in data['value']:
                    unquoted_url = urllib.parse.unquote(
                            values['contentUrl'])
                    url_list.append(unquoted_url)

        for url in url_list:
            try:
                img_path = make_img_path(save_dir_path, url, term)
                image = download_image(url)
                save_image(img_path, image)
                print('saved image... {}'.format(url))
            except KeyboardInterrupt:
                break
            except Exception as err:
                print("%s" % (err))
    else:
        print("Invalid Bing Search API subscription key!")
        print("Please paste yours into the source code.")


if __name__ == '__main__':
    main()

 基本的に以下のURLのbing image search v5でのコードを参考にしています。
Bingの画像検索APIを使って画像を大量に収集する - Qiita

使い方

 まず、subscriptionKeyのところにAzureで作成したBing Image API v7のkeyを代入してください。keyはダッシュボードで確認できます。ここは上記参考URLに詳しいです。
 次に集めたい画像の検索ワードをtermに代入して、保存したいディレクトリをsave_dir_pathに代入してください。今だとデフォルトで次のようになっています。

term = "クラピカ"
save_dir_path = "./hunterxhunter"

 だから上のコードを実行すると、コードが置いてある同じ階層にhunterxhunterというフォルダができて、さらにその下にクラピカというフォルダが生成されてその中に画像が連番で保存されていきます。
 hunterxhunter/クラピカフォルダはこんな感じ。「命をかける」
f:id:mirenn:20171111220342p:plain

ちょっとコードの解説

 今回作成したコードは上記参考サイトのようにひとつの検索ワードで1000件保存するといったものとは違い本当に単純なもので、検索したい画像を35個だけ保存するというものです(取得したURLの画像が指定した画像形式でない場合などには35個以下になります)。35個保存というのはv7のAPIがデフォルトでは検索1回に35個のURLを返してくれて、今回まさに何もいじらずに素直にデフォルトの35個だけ利用しているからです。
 今回のコードでは、APIを呼び出している関数はBingImageSearchという関数だけになります。これは以下のv7の公式ドキュメントから引っ張ってきたものになります。
docs.microsoft.com

def BingImageSearch(search):
    "Performs a Bing image search and returns the results."
    headers = {'Ocp-Apim-Subscription-Key': subscriptionKey}
    conn = http.client.HTTPSConnection(host)
    query = urllib.parse.quote(search)
    conn.request("GET", path + "?q=" + query, headers=headers)
    response = conn.getresponse()
    headers = [k + ": " + v for (k, v) in response.getheaders()
                   if k.startswith("BingAPIs-") or k.startswith("X-MSEdge-")]
    data = response.read()
    conn.close()
    return headers, data.decode("utf8")

 この関数はとりあえず検索したURLが入ったデータを返すものという程度の理解でいいです。とりあえず欲しいデータを返してくれて、それはJSONというデータ形式で渡してくるということが重要です。検索した画像を保存するために私達は、そこから欲しいURLの情報だけ抜き出す必要があります。
 URLの抜き出しの話をする前に、JSONがどういう形で帰ってきているかを見てみましょう。JSONとは以下のようなもので、{}でくくられているデータ形式です。
f:id:mirenn:20171111224239p:plain
 これは、json.load( )に入力してしまえばpythonの辞書配列として扱えるように簡単に変換できます。欲しいURLはvalueの中のcontentUrlというkeyの値なので以下のようなコードになっています。

            for values in data['value']:
                    unquoted_url = urllib.parse.unquote(
                            values['contentUrl'])
                    url_list.append(unquoted_url)

 こうしてurlがurl_listに格納されていきます。欲しい画像のurlが分かっていたらあとは実はpythonではあっという間で、以上が今回書いたコードの大雑把な流れです。

最後に私的反省

私がやったのはAPIを叩く部分を最新のバージョンに合わせたくらいがせいぜいで、他の部分は参考URLのコードをお手本にしています。他にももっと便利な関数がそちらではついていてもっと長いコードだったんですが、こちらではシンプルにBing Image Search API v7をまわしてみる取っ掛かりとして最低限必要なだけに絞ってみました。まぁこれは自分のタスク上1つのけものフレンズにつき1000件も集めることはないしそれぞれとりあえず35件でいいやという妥協がもとです。実際私はこのコードをもとに、最初にけものフレンズの動物名のリストを作ってfor文で回してけものフレンズの画像を収集しました。案の定それぞれ35件まるまる使えそうな画像ばかりというわけではないんですがそれは当たり前ですしまぁまぁ満足しています。
本当はgoogleの画像検索APIがいいな~なんて思っていて、けどそれは使い勝手が悪いらしいということでしょうがなくAzure登録してBing Image Search APIを使うことにしたのですが不都合なところはありませんでした。1ヶ月無料で使えるようだし良いですね。APIころころ変わってなければもっとやりやすかったと思います。まあ十二分に簡単でしたしpythonの勉強にもなって良かったと思います。
どうせそのうちまたAPIが変わってAPIの叩き方も変わるんでしょうが、JSONでデータが返ってくるという形式は変わらないだろうと思うのでそこさえちゃんと分かっていればあとの残りのコードは過去のものが参考になると思います。
でも私の場合画像集めはネットで集めるより動画から切り出したほうが良いよなとか思い始めているのでもう二度とBing Image Search APIについて調べることはないかもしれません……。

さんこま

淫夢漫画:MUR大先輩が家庭教師のとき

f:id:mirenn:20171027212954j:plain

 はい。

 こう疲れた時に手間も暇もかからないやつをネットにあげたくなるわけなんですが、そういうときについ淫夢を使ってしまう悪い癖がつきつつある……。学校疲れたな……時が満ちたら絶対休学してやるぞ。

 

 いろいろしんどいけどお絵かきだけは精力的にしてると思う、ようやくいま描いている漫画も10ページ目突入したし。

f:id:mirenn:20171027213510j:plain

 毎ページころころ絵が変わっちゃうけど絵の上達のためだと割り切ってる。デッサンノートもそろそろ三冊目に突入するしちょっとずつ絵はまともになっている、これは気のせいなんかじゃなくてね。

 それにしても疲れた。

innmuコマンドを作った。

 淫夢学学士論文初投稿です。

イントロ

 innmuとは?→例のアレ

 ざっくりまとめると、コンソール上でコマンドを叩くことでinnmu(一章)を鑑賞できるようにしました。

方法と結果

 動画をテキストにして、そのテキストを表示するexeファイルを作ります。

  まず最初に1章の動画を用意したら、動画を静止画に切り分けます。

f:id:mirenn:20170912232349p:plain

 DeepAAという画像をテキストにする機械学習のコードが公開されているので、それを用いてテキスト化します。DeepAAがテキスト化しやすいように輪郭抽出を行います。

f:id:mirenn:20170912232417p:plain

 これをDeepAAでテキスト化しました。

f:id:mirenn:20170912232607p:plain

 材料は揃ったのでテキストを読み込んで表示するコードを、visual studioで書きます。コンパイルしてexeファイルを作成し、PATHが通ったところなどに置いて完成です。

 

 実行してみます。innmu、と叩きます。

f:id:mirenn:20170912233221p:plain

 そして、一章が流れ始めます!全部をここには紹介しませんが、できた動画の一部分を紹介します。

 まずこれが、冒頭です。タイトルの後にTNOKの乗っている車が後ろから追突されて振り向くシーンです。

f:id:mirenn:20170912233433g:plain

 次はこれ。オナシャスセンセンシャルと言われた後のTNOKです。TNOK「とりあえず犬の真似しろよ」と言うところ。いい動きしていますね。

f:id:mirenn:20170912233624g:plain

 これらの場面はうまくいっているほうで、場面によっては元の動画を知っていても何がなんだかよくわからないという箇所も少なくありませんでした。

f:id:mirenn:20170912234224p:plain

 特に静止画一枚を取り出した場合、それ一枚を見ただけではわからないことがほとんどです。動画を見ることでこれがなんなのかわかります。実は、これもTNOKがイスに座っているところ(だったかな)。

むすび

 これでいつでもコンソール上で淫夢を見ることができるようになりました。例えばブラウザを開けないようなときでも見れるわけです、ただしwindowsじゃないと多分動かないところが課題。いつか世界中のPCに入れてviやemacsなどのコマンドをinnmuコマンドでエイリアスすることで、戦争のない世界を目指したい。

トーヴ湿原の怪物[漫画]

漫画:トーヴ湿原の怪物

 

f:id:mirenn:20170909175704j:plain

f:id:mirenn:20170909175708j:plain

f:id:mirenn:20170909175713j:plain

f:id:mirenn:20170909175717j:plain

f:id:mirenn:20170909175722j:plain

f:id:mirenn:20170909175726j:plain

f:id:mirenn:20170909175731j:plain

f:id:mirenn:20170909175735j:plain

f:id:mirenn:20170909175740j:plain

f:id:mirenn:20170909175744j:plain

f:id:mirenn:20170909175749j:plain

f:id:mirenn:20170909175753j:plain

f:id:mirenn:20170909175757j:plain

f:id:mirenn:20170909175802j:plain

 

 今回も描いた漫画について百行くらい書きたいところですが、つらつら書いている暇あったら手を動かせってそれ一番言われてるから。当分、書き溜めてしまったネームを消化するだけで毎日が終わっていく予定です。過去の自分を殴りたいし、なんかまた徐々に増えていってるんだが?

 

 最近ポケモンGOを始めました、とても面白いです。ポケモンGOをやっているうちに保育園児だったころの自分を思い出しました。

 保育園児だった私はアニメのポケモンがとても好きだったので夜寝るときに願いました、朝起きたらポケモンがいる世界になっていますように、と。そして翌日朝起きてポケモンがいないことにがっかりする。

 今はスマホを通して街を見るとポケモンがいます。子供のときの望みが大人になったら思いもしなかった形で叶っていたというわけです。案外子供のときの願いは、それを願ったことを覚えていないだけで3っつ4っつは勝手に叶っているのかもしれない。

おじいさんのハンドスピナー[漫画]

漫画:おじいさんのハンドスピナー

 

f:id:mirenn:20170827200717j:plain

f:id:mirenn:20170827200723j:plain

f:id:mirenn:20170827200730j:plain

f:id:mirenn:20170827200734j:plain

f:id:mirenn:20170827200740j:plain

f:id:mirenn:20170827200750j:plain

f:id:mirenn:20170827200756j:plain

 

 

 

 

 今回の漫画について以下に描きます。長くなっちゃうね。

  • 漫画の習慣の話

 漫画の描き方をちょっとずつ習慣レベルで変えていっています。最適化が段々達成されてる。

 つい最近までは、朝にネームを最低1p以上描くということをやっていたんですが清書待ちの下書きの漫画が増えていっこうにネットにアップできなくなっていました。あれもこれも手を付けて収拾がつかなくなってました。うええ……。

 漫画を描くということ自体には慣れてきたので、ここで路線を変更することにしました。ネームができたら次のネームに取り掛かる前に、そのネームを完成原稿にすることに決めました。

 些細なことに思われるかもしれませんが、清書待ちが恒常的に溜まっているというのはなかなか精神的にも物理的にも悲惨なのです。具体的にどういうことかというと、清書しかけの漫画はすぐ続きを清書できるようによく見える場所に放置します。つまり床に紙がむちゃくちゃ並び、ただでさえ狭い下宿の空間を逼迫します。精神的にもよろしくない。きっちり一個ずつ完成させていく方がやっぱ楽しいですしね。

  • 今回の漫画の話 

絵について

 今までよりも絵をしっかり描いています、それは背景を増やしたというだけではありません。ホームセンターにいって大きめの鏡を買ってきて、それを使って描きにくい箇所は鏡を見ながら自分でポーズをとって描いています。そうすると画力の底上げになるし、絵の練習にもなって良いことばかりです。どうでしょう、段々前より絵がましになってきてる感じがしませんか? 私はそれがために最近楽しいです。

お話について

 お話に関してですが各箇所で自分の好みを反映しているなぁ、と見返してさらに思います。以前、自分の中の原風景を漫画にしているんだろうか? という質問を見たんですが私に関しては全くその通りです。自分が今まで感動してきたものをアウトプットしています。

 今回の話では大きく3つくらいの作品を思い浮かべていました、どれも私の好きな作品です。凧あげ(子供の遊び)で連想していたのは川原泉の「ヴァンデミエール 葡萄月の反動」でありゲイラカイトはここから取ってきましたし、意地の悪いおじいさんという設定は「天才柳沢教授の生活」の小話のひとつに出てくるキャラクター。少年が爺さんに対して第一印象は最悪というところでは「四畳半神話大系」の「これが小津とのファーストコンタクトでありワーストコンタクトであった」を思い浮かべていました。作品全体の着想でいったら、大きな古時計という歌になります。今回採用しなかった展開も考えあわせると、他にもいろいろな漫画や映画を参考にしています。

 また話の展開の取捨選択もましになってきたなと思っています。今回、考慮する枝分かれが最も多かったのはラストです。終わり方で他に考えていた展開でいうと、主人公がボロボロ涙を流しながらクソジジイと呟くとか、むすっとしていたお爺さんの遺影が笑ったように見えるとか、誰にも回されなくなったハンドスピナーを奥さんがおじいさんの死んだ後も回し続けるとかになります。

 採用しなかった展開はそれなりの理由があります。例えば主人公がボロボロ涙を流しながらクソジジイと呟く場合はその展開があまりにウェットで、自分の趣味ではないからです。色々な可能性を考慮したうえ、今の形のもっと爽やかで淡泊なラストになりました。

 今回ハンドスピナーがメインの小道具ですが、これは私が最近ハンドスピナーを買って感動したからです。先に述べたように感動したものでお話を作りたくなっちゃうんです。ハンドスピナーはただ回転させるというとても無意味な玩具であり、しかし洗練された美しさがあります。感動しました。これほどわけのわからん工業製品は見たことがありません、百年前の人にこれをポンと渡してもどうやって使うかわからないんじゃないでしょうか。まさかただの玩具なんて。

 ハンドスピナーには「大きな古時計」の歌のように少しセンチな話が似合うと思ってこういう話を組んだわけですが、ハンドスピナーの出自を調べるとやはりちょっと悲しめで健気な話が似合うようです。*1

 

 

 

 最近の所感

 サークルにはエロ漫画を描く人が少なくないようですが、私は今のところあまりドスケベ漫画を描こうという気力が湧きません。というのは多分、今のところエロ漫画で感動した記憶がないからなんでしょう。

 それはまぁつまり、私がエロ漫画を描き始めたらそれは何かのエロ漫画に感動したということであり、かつエロ難しい構図は自分でポーズを取って鏡を見ながら描いているということです。エモそう。

 ……そろそろ、院試。明後日くらい……。

*1:ハンドスピナーは重症筋無力症を負った母親が娘と遊べる玩具として考案したらしい。

タクシー経由空港着[漫画]

漫画:タクシー経由空港着

 

f:id:mirenn:20170808092534j:plain

f:id:mirenn:20170808092539j:plain

f:id:mirenn:20170808092544j:plain

f:id:mirenn:20170808092549j:plain

f:id:mirenn:20170808092554j:plain

 

 5pで前後するくらいの漫画を描いていこうと思って描いたものですが、5pでも十分きつい。なにがきついって絵を描く量ですね。描くスピードがめちゃくちゃ遅いので、単純に清書する時間がのびのびていく。助けてドラえもん

 次はバトル漫画を描く予定で、次の次はギャグ漫画になりそうです。

 今回の漫画の感想として、この話は以前よりずっとストーリーめいてきて良くなってきたかなと思っています。基本的に一話で完結するギャラリーフェイクという美術品を扱った漫画がとても面白くて、それに少しでも近づけたらと思って描きました。ギャラリーフェイクは勉強になりますね、面白い漫画のエッセンスが詰まっていると思います。

 ところでギャラリーフェイクと聞いて、コルトピだ!と思ったそこのあなた。ハンターハンター好きみたいですね。コルトピ……。

 もっと早く描けるように精進!

人類最後の恋をしよう[漫画]

漫画:人類最後の恋をしよう

f:id:mirenn:20170807191253j:plain

f:id:mirenn:20170807191300j:plain

f:id:mirenn:20170807191310j:plain

 

 今回は自分の勉強のことについて、あれやこれやと書いていきます。

 最近ちょこちょこ基本情報技術者試験の問題をやっています。選択問題なので暇なときかつ意識が高まったときにやってます。今のところわりと面白いというかこの手の勉強を体系的にやってないので、本当にこれ基礎的で知っておくべきことなことなんだろうなところを抑えられて良いなと思っている。秋期の申し込み期限が迫っているけど、他にやることいっぱいあるなかで実際に受けるかどうかは迷いどころ。この試験よりもまず私は検査技士の国家試験の勉強をしなきゃいけないのでは?と思いつつ、基本情報技術者過去問道場|基本情報技術者試験.comでポチポチ選択問題を解いている。

 

 Twitterに張り付いていると競技プログラミングをやっている人のツイートがまわってきて、新しい競技プログラミングの本が公開されているというので興味本位で見てみた(Competitive Programmer’s Handbook : https://www.cses.fi/book.pdf)。まだ序盤も序盤であるソートのところまでしか目を通していないけど丁寧な書き方でわかりやすい。maximum subarray problemという問題を効率よく解く手法のとこなんかは読んでいてへ~と思うところがあって、アルゴリズムの奥深さの一端に触れた気がする。ソートなどは競技プログラミングにとどまらない一般な計算機科学の話だが、競技プログラミング特有だろうなという内容の説明も面白かった。競技プログラミングではコードをなるだけ短くすることが正義だという趣旨のところで、defineを使ってfor文をもっと短く書く手法を紹介していた。私はそんな定義の仕方を初めて見たので、目新しく見えて新鮮だったというかすごい激しい書き方だなと思ったんですけど一般的なんでしょうか。この本は暇なとき眺めるのに悪くなさそう。まぁ、ほどほどに読みたい。

 大学院でやることは画像処理関係なのでもうちょっと画像処理に関わる本を読むべきだなと思って、画像処理研究者おすすめの本を探してきて見つけたのがProgramming Computer Vision with Python(http://programmingcomputervision.com/downloads/ProgrammingComputerVision_CCdraft.pdf)。ドラフトが無料で公開されているのでそれをぼちぼち読んで結構よさそうだったので、結局日本語訳版*1を買うことに決めました。書かれているコードがpython2系なので古い言語を使っているという点を気にしなければ、画像処理の世界を一望できて実際に手元で試せるというところがいいところ。画像処理っていったいどんな道具があるの? ってことをざっくり一通り知れて、それだけでも買った意味はあったと思っている。動体検知、セグメンテーション、特徴量、などなどをどうやって実装するか知れて満足。まだ、実際にコードを実行していないけどこの本の内容を使って遊ぶことで動画が作れないかと日々頭を悩ませているところ。オプティカルフローという動きの検出をする手法は、見た目にもわかりやすくて動画映えしそうで良いななんて思ったりはしているんですがね。

 

 ああ、新しい動画の構想はさておき早く動画完成させるぞ――。研究室の先輩に私のくだらないyoutubeの動画を観られてしまって、早く作れとせっつかれてしまっている。今作っているものがぼちぼち進んできたので、そろそろ出せるといいな……。

*1:OpenCVを使うだけではコンピュータビジョンの本質を理解できません。forループでピクセルを操作し行列を計算する時代でもありません。Pythonの数値演算ライブラリを使えば、ほどよい粒度でコンピュータビジョンの基礎を学べます。』という日本語訳版の煽り文句にも大いに惹かれて買ってしまった。こんなこと言われたら気になって買っちゃいますよ