MHRNOTE

画像処理、コンピュータービジョン、GPGPU、開発環境、etc

pyCUDAで画像処理に挑戦してみた(超入門編)

どうもこんにちは。

ちゃんとブログを更新しようと思っている今日この頃です。

前回の記事で、GPU環境をいい感じに整えることができるnVidia-dockerについてご紹介しました。

mhr380.hatenablog.com

せっかく作って壊せるGPU環境が手に入ったので(?) 今回は、CUDAを使ったGPUプログラミングをやってみたいと思います。 ところで、CUDAで遊んでみたいけど、C++から叩くのは面倒なあ… という方は結構いらっしゃるのでは無いでしょうか。 もちろん、僕もそのタイプです。

そんな僕にピッタリのツールとしてpyCudaがあります。

Welcome to PyCUDA’s documentation! — PyCUDA 2017.1.1 documentation

PythonからCUDAのカーネルを呼ぶことができるライブラリです。しかし、情報があまりないんですよね…

今回は練習がてら、numpy array形式の画像をGPUで処理するようなコードを書いてみたいと思います。

環境

以下の環境で動作確認をしています。

準備

nVidia-dockerのコンテナとして環境を用意することにします。 ベースとして、以下のイメージを利用しました。

https://hub.docker.com/r/nightseas/pycuda/

ただ、上記イメージにはPillowが入っていなかったので足してあげます。 私の環境では、下記Dockerfileをビルドすると正常に動作しました。 ただし、ベースとしたイメージはPython2.7.12だったので、3系を使いたい方は別途環境を構築する必要がありそうです。 私もそのうちやってみます。

FROM nightseas/pycuda:latest

RUN apt-get update && \
    apt-get install -y python-tk

RUN pip install pillow

やってみること

最初は基礎的なところから始めましょう。 8bitグレースケール画像の輝度を反転するだけのコードを書いてみたいと思います。 f:id:mhr380:20170828224249p:plain

実装

# coding: utf-8

import pycuda.autoinit
import pycuda.driver as cuda
from pycuda.compiler import SourceModule

import numpy as np
from PIL import Image


#
# 以下CUDAカーネル
#

mod = SourceModule("""
__global__ void negative(unsigned char *image, int height, int width)
{
    int pos_y = threadIdx.y + blockDim.y * blockIdx.y; // 画像のy方向の位置を取得。
    int pos_x = threadIdx.x + blockDim.x * blockIdx.x; // 画像のx方向の位置を取得。

    int idx = pos_y * height + pos_x; // 内部では1次元配列化しているので、インデックスはこのようになる

    image[idx] = 255 - image[idx];  //画素値を反転
}
""")


#
# 画像の読みこみ
#
pil_image = Image.open("./lena_gray.png")   # 画像の読み込み
image = np.array(pil_image, np.uint8)       # PIL Image型からnumpy arrayに変換。

height, width = image.shape[:2] # 今回は512x512pxの画像を利用

#
# CUDA周りの準備
#
cuda_kernel = mod.get_function("negative")  # 上で定義したカーネルを呼び出す

block   = (512, 1, 1)                       # Block size (後述) 
grid    = (512, 1, 1)                      # Grid size (後述)


#
# カーネルを実行
#
cuda_kernel(cuda.InOut(image),              # imageを参照渡しする。
            np.int32(height),               # int型定数はnumpyで明示的に型を定義する
            np.int32(width), 
            block=block, grid=grid)         # BlockとGridも引数として与える


#
# 処理後の画像を保存
#
pil_output_image = Image.fromarray(image)
pil_output_image.save("out.png")

CUDAにおける画素インデックスは、下記スライドのp59を見るとわかりやすいと思います。

www.slideshare.net

こんな感じで初歩のの初歩ですがCUDAを使った画像処理を行なうことができます。 次回(あるのか?)は、もうちょっと凝ったことにチャレンジしたいと思います。

2017/08/29追記 qiita.com Gridsize, Blocksizeは上記の記事を参考に修正しました。

もう依存関係に悩まない! nvidia-dockerでクリーンなGPU環境を作る

こんにちは. 前回の更新が2015年5月,今日は2017年8月の終わりです. 察していただけますと幸いです(?)

突然ですが,Dockerって便利ですよね. www.docker.com

数年前から話題になっているDockerですが, 恥ずかしながら,最近まで使ったことがありませんでした. しかし,いざ使ってみると,ライブラリのインストールとかでいちいちハマっていた時間を大きく短縮でき,新しいライブラリを試してみるまでの障壁が低くなりました. もちろん,Dockerならではのめんどくささみたいなのもありましたが,全体的に便利になったと思ってます.

ところで,色々なライブラリがリリースされ,それぞれ触ってみたいなーと思うカテゴリにDeep Learningがあります. 有名どころだけでもCaffe, Tensorflow, Chainer, Keras, PyTorch…なんて具合です. が,依存関係やらGPU周りのめんどくささで,インストールが面倒だと言う話はよく耳にするかと思います.

ということで,本記事ではOS未インストールのGPUマシンから,nvidia-dockerを使ってクリーンなGPU開発プラットフォームを作るまでを解説したいと思います.

はじめに:nvidia-dockerとは?

dockerのプロセス上からnVidiaGPUへのアクセスを可能にするユーティリティです.  github.com

$docker [command]$nvidia-docker [command]に置き換えるだけで,GPUへのアクセスが可能になるスゴいヤツです.

https://cloud.githubusercontent.com/assets/3028125/12213714/5b208976-b632-11e5-8406-38d379ec46aa.png

環境構築

Ubuntuのインストール

当たり前ですが,OSをインストールしないことには始まりません. が,GPU付きUbuntuのインストールって面倒なんですね. まさかここで詰むとは…という感じでした.

まずUSBメモリにUbuntu16.04 LTSのisoを焼き,ダイアログ通りインストールします.インストール完了後に再起動をすると,なんと起動しないではありませんか!

この問題と解決策は,様々な方が記事にしてくださっています.

参考文献1 qiita.com

参考文献2 https://www.cs.gunma-u.ac.jp:443/~nagai/wiki/index.php?trouble%20shooting%20–%20%A5%B0%A5%E9%A5%DC%C1%FD%C0%DF%A4%B7%A4%C6Linux%20install%20%A4%B7%A4%BF%A4%E9%B9%F5%A4%A4%B2%E8%CC%CC

2017/08/28追記: なぜかうまくリンクが貼れないので,「trouble shooting – グラボ増設してLinux install したら黒い画面」で検索していただけるとヒットすると思います.

問題は,nVidiaグラボとUbuntuブートローダの相性が悪いということらしいです… 私は参考文献2の方法でインストールしました. 要約しますと

  • とりあえずUbuntuをインストール

  • インストールディスクを取り外さず,再起動

  • インストールディスクを起動し,「Try Ubuntu without installing」で"e"キーを押下

  • ブートローダの種類を"quiet splash"から"nomodeset"に変更する

  • F10で実行すると,Ubuntuの試用版が起動

  • 最初にインストールしたUbuntuを,試用版Ubuntuにマウント

  • マウントされたパーティション内のブートローダに関する設定を書き換え(詳細は参考文献に譲ります)

  • アンマウントの後,再起動をかけるとUbuntuが立ち上がる

というような手順になります.なんでこんな面倒なの…

UbuntuにCUDAのドライバをインストール

こちらのブログを参考にさせていただきました.

blog.amedama.jp

まず,下記のCUDA Websiteから.debのダウンロードリンクを取得します.

CUDA Toolkit Download | NVIDIA Developer

ここで,OS > Archtechture > distribution > Version > Installer type(deb(Network))と選択します. Ubuntuにインストールする場合は,下記のような選択になるかと思います. f:id:mhr380:20170826162530p:plain

その後は,指示通りに

$ wget [上記サイトの"download(2.6kB)"のリンク先] 
$ sudo dpkg -i cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
$ sudo apt-get update
$ sudo apt-get install cuda

を実行してあげることで,特にハマることなく完了できました. nvcc -Vなどでインストールが正しく完了しているかがわかります.

Dockerのインストール

Dockerをふつうにインストールします.ややハマりどころがありました.

 $ sudo apt-get install Docker.io
 $ sudo usermod -aG docker  `whoami` # sudo無しでDockerを動くようにする

すると,Dockerが立ち上がらないのですが,下記URLにあるように,Ubuntuを再起動すれば解決しました. qiita.com

次に,(私の環境では必要があったので)Dockerにプロキシを設定します. ちなみに,Dockerの設定に書くプロキシサーバのURLがどうも名前解決がされず,IP表示で全て書いています. これは環境によるのかDockerの問題なのかはわかっておりません.ご存じの方はおしえてくださると幸いです. 以下では,プロキシサーバのURLをxxx.xxx.xxx.xxx:xxxx/ とおくことにします.

docs.docker.com

$ mkdir -p /etc/systemd/system/docker.service.d

$ touch /etc/systemd/system/docker.service.d/http-proxy.conf

作成したhttp-proxy.conf内にプロキシサーバのURLを書いてあげます.

 [Service]
 Environment="HTTP_PROXY=http://xxx.xxx.xxx.xxx:xxxx/"

同様にhttp-proxy.confも作成します.

 [Service]
 Environment="HTTPS_PROXY=http://xxx.xxx.xxx.xxx:xxxx/"

Dockerを再起動します.

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

これでDockerのインストールは完了です.

nvidia-dockerをインストールする

ご存じの方も多いと思いますが,通常のDockerからはGPUにアクセスすることができません. こんなときに用いるのがnvidia-dockerです. nvidia-dockerのインストールも,ハマりどころは少なかったです.

$ wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.0-rc.3/nvidia-docker_1.0.0.rc.3-1_amd64.deb `
$ sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb`

コレだけの手順で動くはずです.とりあえず動かしてみましょう. CUDA入りのUbuntuイメージをpullし,GPUの状態を確認するnvidia-smiを実行してみます.

nvidia-docker run nvidia/cuda nvidia-smi

するとこんな感じになりました.DockerプロセスからGPUにアクセス可能なことが確認できました. f:id:mhr380:20170826154429p:plain ちなみに通常のDockerだと

$docker run nvidia/cuda nvidia-smi
bash: nvidia-smi: command not found

になってしまいます.

以上で環境構築は終わりです. 後はDocker Imageを立ててGPUをぶん回すだけですね. それでは皆様,楽しいGPUライフを!

【Python】ササッとTwitter Botを作ってみる

f:id:mhr380:20150518215430p:plain ゆるいプログラミングネタです. PythonTwitterに天気を自動投稿させてみました. 今更感もありますが……

使ったもの

下ごしらえ

pip install python-twitterpython-twitterを導入します.

つくる

TwitWeather.pyという名前で作成しました.

アタマ

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import twitter
from system import conf # ./system/conf.pyにAPIキーなどを記述しています.
import urllib # 天気取得時に使用
import urllib2
import json

# const
api = twitter.Api(
        consumer_key        = conf.consumer_key,
        consumer_secret     = conf.consumer_secret,
        access_token_key    = conf.access_token_key,
        access_token_secret = conf.access_token_secret,
        )
# system/conf.pyからAPIキーなどを読んできます.        

天気取得

def get_weather():
    city = 'Ikoma,jp'
    url = 'http://api.openweathermap.org/data/2.5/forecast/daily?'
    query = {
            'q': 'Ikoma,jp',
            'mode': 'json',
            'units': 'metric',
            'cnt': '1',
            }
    req = urllib.urlencode(query)
    res = urllib2.urlopen(url + req)
    line = res.readline()
    #print line

    desc = []
    desc.append(city)

    weather = json.loads(line)['list'][0]['weather'][0]['description']
    desc.append(str(weather))

    desc.append('min:')
    temp_min = json.loads(line)['list'][0]['temp']['min']
    desc.append(str(temp_min))

    desc.append('max:')
    temp_max = json.loads(line)['list'][0]['temp']['max']
    desc.append(str(temp_max))

    return desc            

天気の取得にはOpenWeatherMap current weather and forecastを使いました.

クエリ http://api.openweathermap.org/data/2.5/forecast/daily?q=Ikoma,jp&mode=json&units=metric&cnt=1を投げると

{"cod":"200","message":0.0585,"city":{"id":1861749,"name":"Ikoma","coord":{"lon":135.699997,"lat":34.683331},"country":"JP","population":0,"sys":{"population":0}},"cnt":1,"list":[{"dt":1431914400,"temp":{"day":16.89,"min":16.45,"max":16.89,"night":16.45,"eve":16.89,"morn":16.89},"pressure":995.29,"humidity":96,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":1.59,"deg":120,"clouds":92,"rain":6.9}]}

が返ってきます.

ちなみに,jsonをパッと確認する際にはJSON整形サービスをよく利用させてもらっています. 今回は 天気最低気温最高気温 を表示したかったので上記のようにしています. それらの情報をdescとして返しています.

ツイート

def tweet(desc):
    content = '.@mhr380'
    for item in desc:
        content += ' ' 
        content += item
    content += ' """Tweeted by Bot"""'

    print content
    status = api.PostUpdate(content)                             

先ほど返したdescをツイートしますが,コードはこれだけです. APIにツイートしたい内容を渡してあげるだけです.

APIキーなど

https://apps.twitter.com/app/newに必要事項を入力するとAPIキーがもらえます. それを,system/conf.pyに記述します.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
consumer_key           =  'xxxxxxxx'
consumer_secret       ='xxxxxxxx'
access_token_key     = 'xxxxxxxx'
access_token_secret = 'xxxxxxxx'

herokuの準備

今回はheroku上で動かします.設定ファイルとしてProcfile(拡張子なし) とrequirements.txtが必要になります.

まずProcfile

bot: python TwitWeather.py

のように記述します.

次に,requirements.txtには,用いた外部ライブラリおよびそのバージョンを記述します. 今回使用したライブラリはpython-twitterのみで,そのバージョンはpip listで調べることができます. で,requirements.txtには

python-twitter==2.2

と記述します.

デプロイ

heroku上にデプロイします.ご存知の通り,デプロイにはgitを用いるので,コミットしておきましょう. なお,手順などはこちらのブログ記事を参考に行いました.

firstspring1845.hatenablog.com

完成?

こんな感じで,勝手に天気をツイートしてくれます.

今後

(もうちょいテクノロジー感のあることをしたい)

参考

Python&Qtでアプリを作って,.app形式にするまでのメモ

f:id:mhr380:20150310201750p:plain 最近ようやくGUI的なものを作るようになったので,一連の流れを備忘します. お題は,研究で使おうかなーと思っていた画像データセット*1を展開するためのアプリです. (もちろんGUIである必要性などは全くないですが……)

使ったもの

メイン

  • Python 2.7.9
  • Python-Qt4(GUIフレームワーク
  • Py2app(.pyファイルを.app形式に変換してくれるすごいやつ)
    • インストールは pip install py2app でOK.

アプリの目的のために使ったもの

つくる

作ったものはこちら.

GUI部と演算(?)部は分けております.

出来上がりはこんな感じ. f:id:mhr380:20150310191458p:plain f:id:mhr380:20150310192821p:plain

.app形式に変換する

こちらの記事を参考にしました. http://www.atsuhiro-me.net/python/dev/py2app

  • まず, pip install py2app で,py2appをインストール.
  • py2applet --make-setup hd5Extractor_gui.py を実行すると,'setup.py'が生成される.
    • ちなみに自動生成された'setup.py'の中身はこんな感じ.
  • python setup.py py2app -Aにより,ビルドを実行.
  • ./dist/./build/が生成され,./dist/の中に.app形式の実行ファイルが生成される.

みたいな流れになっております.

こんな形でプログラムを固めておくと,スクリプトがある場所を探す手間もなく,いつものアプリケーションを開くように実行することができ,結構ラクだなーと思ったりしています.

OpenMPとOpenCVを使って書いたC++のコードをCMakeでビルドするときのメモ.

お久しぶりです.(すごくお久しぶりです)

OpenMPOpenCVを使って書いたC++のコードをCMakeでビルドするときのメモ.

の前に.

CMake

CMakeはコンパイラに依存しないビルド自動化のためのフリーソフトウェア*1

いろんなライブラリを使ったソースをコンパイルするときにMakefileを頑張って書くのは面倒なので,ありがたく使っています.

使い方としては,CMakeLists.txtに 「◯◯というライブラリを使いたいからどうにかして!」みたいなノリで書いて,cmake <path/to/CMakeLists.txt>を実行すると,うまいこと(大量の中間ファイルを 吐き出しながら)Makefileを生成してくれます. なお,最初に参考にしたのはこちら *2 *3

OpenMP

OpenMPは、並列コンピューティング環境を利用するために用いられる標準化された基盤。OpenMPは主に共有メモリ型並列計算機で用いられる。*4

とても小さな労力で並列化が可能ということで,最近使い始めました.

CMakeLists.txt

で,こちらがOpenMPOpenCVに対応したCMakeLists.txtです.

cmake_minimum_required(VERSION 2.8)
project( sample )
find_package( OpenMP )
if (OPENMP_FOUND) 
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
find_package( OpenCV REQUIRED )
add_executable( sample  sample.cpp )
target_link_libraries( sample ${OpenCV_LIBS} )

このあとにmakeでビルドしてあげると,簡単に実行ファイルを生成することができます.

*1:出典:Wikipedia「CMake」

*2:http://yosilove.blog.shinobi.jp/プログラム/cmakeの使い方メモ

*3:http://qiita.com/termoshtt/items/539541c180dfc40a1189

*4:出典:WikipediaOpenMP

関西CVPRML勉強会で知見を広げてきた

こんばんは、ミハラです。 10/4(土)に大阪府立大i-siteなんばで行われた「関西CV・PRML勉強会(@kansaicvprml)」へ初参加してきましたので、その感想をつらつらと。

内容

今回は、CVPR,ECCVで発表された最新論文の紹介を通して、 普段の研究・開発分野とは異なる技術について知る機会にしようという趣旨だそうです。

4名の発表者が最新論文についてプレゼンすると聞いてワクワクしていたのですが、 色々あって*1発表することになってました。

以下に概要をば。

発表1

最初は@lachesis1120さんによる領域分割をめっちゃ早くやろう! というCVPR2014の論文*2のご紹介。

領域分割計算を高速に行うために、求めた領域から効率よく物体候補を算出するGroupingアルゴリズムの提案で、 画像ピラミッドを生成し、いろんなスケールでの領域分割を行い、別々に一般的な手法で領域分割を行って、「だいたいこのへんが境界だろう」という情報を統合してから、詳細な分割を行うというのが概要だったように思います(合ってますかね?)。

ただやっぱり情報量が増えるので、固有ベクトルの計算を高速化してあげようというのがすごいところだそうで。

発表2

続いては@progranateさんのご発表です。手ブレ画像を復元する技術はすでにありますが、縞状アーティファクト(リンギングと呼ぶ)が発生してしまいますが、それを検出して綺麗にしてあげましょう! という論文*3のご紹介でした。

デコンボリューション自体は色々な手法があるわけですが、この論文では検出に重点を置いていたようで。 なぜ手ブレを除去するとリンギングが発生するか、どうやってそれを取り除くか、などの説明がわかりやすくて、お手本にしたい発表だと終始感じておりました。

あと、ECCV参加記がとっても面白かったです。

(ヨーロッパではSuperdry 極度乾燥(しなさい)という洋服ブランドが流行っているとか)

発表3

我が師匠(勝手にそう思ってる)田中さんのご発表は、プロジェクタでパターンを投影してあげると測距・法線・BRDF計測など色々なことができますが、プロジェクタの被写界深度が浅いため、限られた領域でしかうまく測定ができないという問題や、半透明物体のようなものの測定誤差を改善しよう! という論文*4でした。

パターン投影で3次元復元をしようと思ったら、やっぱり半透明物体は距離推定の誤差が大きくなるわけですが、この手法ではそういうミスを減らしてあげているようで、結構正確に推定がなされていてびっくりでした。

発表4

私の発表でした。 ライトフィールドを取得して、邪魔な遮蔽物を消してしまおう! という論文*5の紹介をさせていただきました。

発表5

@beat_itudeさんのご発表で、 写真に写っている人が着用している服などをピクセル単位でラベリングしてあげよう! という論文*6のご紹介でした。 ICCV2011で提案された進化版SVMみたいなやつ*7を適用していたりと 最新技術ゴリゴリでしたが、わかりやすく解説していただき、機械学習系に関して知見が深まりました。 しかし、結果として示されていた衣服推定精度はすごいと感心せざるを得ませんでした。

まとめ的な

聴講した感想

皆さんわかりやすくご発表していただいたのですが、やっぱり自分の数学力・機械学習系の知識の無さで、全てを理解することができなかったというのが正直なところです。 しかし、確実に知識の裾野は広がったなというのが感想です。 最適化・固有値問題・(余裕があればPR・ML)について理解を深めていかなきゃと感じました。 (追記) データ項+スムースネス項のエネルギー最小化問題が定番手法になってるので、使いこなせるようにならなきゃ……

自分の発表

発表者がDホルダー、D学生で私だけM1だったので、皆さんと比較して発表の質は低かったなと感じました。 やっぱり論文中の数式が全然追えてなかったのですが、あの場では皆さん気になるところだったかと思いますので、ちゃんと理解して説明するべきだったなというのが心残りです。

その他

懇親会たのしかったです。色々ごちそうさまでした。

最後に

次回も参加します! (発表するかは未定)

あと、記事に掲載した発表概要に誤り*8があればぜひ教えていただきたいです。

*1:太陽の塔の近くの研究室から怪電波を受け取った

*2:P. Arbelaez, et al. , "Multiscale Combinatorial Grouping", CVPR, 2014

*3:A. Mosleh, et al., Image Deconvolution Ringing Artifact Detection and Removal via PSF Frequency Analysis, ECCV2014

*4:S. Achar, et al., Multi Focus Structured Light for RecoveringScene Shape and Global Illumination, ECCV2014

*5:T. Yang, et al., All-in-Focus Synthetic Aperture Imaging, ECCV2014

*6:W. Yang, et al., Clothing Co-Parsing by Joint Image Segmentation and Labeling

*7:T. Malisiewicz, et al., Ensemble of Exemplar-SVMs for Object Detection and Beyond, ICCV2011

*8:10.6 一部修正しました