みなさん、おはこんばんにちはでス。普段は動画処理とかffmpegまわりの記事を投稿してます。「ス」です。2014年もあとわずか 年末年始とのんびり過ごしたいものです のんびり過ごすなら、やっぱり、画像処理・動画処理が定番ですよね?そこにあるCPUを何故100%まで使い切らないのか?山があるから登るのが登山家の心がけなら、CPUがあるなら使い切るのがプログラマーとしての正しい心がけです。使い切る中で最適化を目指す!では、使い切るにはどうしたらいいのか?
画像処理だ!!!
動画つくるだ!!!
最近は使えるCPUの数が増えてきましたね。少し背伸びをすれば、かつては想像できなかったほど使えそうです。Hadoopです 分散処理です。プログラミング楽しいですね。夢は広がります。CPUは寝かせただけ計算機会を失っているのです。とにかく計算。え?プログラミングができない?それなら、3Dバリバリのオンラインゲームをどうぞ。え?最近はGPUだって?・・・石があったら熱するということで、キットカットおいしいね
ところで、RMagickに限らず画像・動画処理ツールを使ったことはありますか?ウェブアプリケーションをつくっているなら
- 画像サイズの変更:サムネイル用とか
- フォーマットの変更:jpegをpngにするとか
- デコ:文字入れしたり、スタンプ入れたりとか
といった感じの用途がメジャーかな。たいていのもの(ソフトにしろライブラリにしろ)には
- ガウスぼかし
- 2値化(白黒にする)
- エッジ抽出
といった謎機能がついていて、「これ何に使うんだろう?」って思うことありません?普通のプログラミング生活からは縁遠い、画像認識(顔とか文字とか物体とか)とかやるときに効いてくる処理だったりします。もし、暇があったらその辺の世界も覗いてみてね。面白いよ。りさんふーりえ云々とかうぇーぶれっと云々とか
RMagickとその他便利なものを使いながら何かつくる ねたは、Ruby Advent Calendar 2014 。12月1日時点でカレンダーに載っている情報使う。誰かばっくれても消さない、ごめんね。
書いていくよ
つくるもの
Rubyのアドベントカレンダ2014の登場人物を使った何かをつくります。
できあがり
先に出来上がったものを↓こちらです
準備
Ubuntu12.4系を使います。Windowsでは時々RMagickのインストールがうまくいかないことがあるらしい。いつものことだね
$5/月から使える DigitalOcean というクラウドサービス使ってます。今なら紹介プログラムで $10 分無料で使えます。
それでもWindowsに入れたい場合には
64bit windows でも 32bit 用のImageMagick入れてねとか、インストール時のオプション指定とか気を付けた方がいいことがあるようだ。以下 windows7 にインストール際に参考になるページ ※いずれも最新バージョンの情報ではないので自己責任で
- http://codeofalice.com/code/installing-rmagick-on-windows-7/
- http://www.ownway.info/Ruby/index.php?rmagick%2Fhowtoinstall%2Fwindows
- http://uisteven.blog.fc2.com/blog-entry-37.html
なんかブクマで・・・
RMagickはちょっと前に試したけどWindowsでビルド出来なかったんだよなあ。結局ImageMagickコマンドを呼び出して使ったけど、今はビルドできるんだろうか。
こんなコメントが付いた。Windows 7 に入れてみた。ビルドできた。
ImageMagick のインストール
sudo apt-get install imagemagick
RMagick のインストール
sudo gem install rmagick
RMagick のドキュメント
http://www.imagemagick.org/RMagick/doc/
RMagick の github レポジトリ
https://github.com/RMagick/RMagick
つくるよ
つくりかたの手順
こんな感じでつくります
- Advent Calendar の参加者一覧を取得 Nokogiri でスクレイピングして .json で保存 (記事下部のおまけにサンプルコード有)
- qiita.com API v2 を使使用し参加者の情報を取得 (記事下部のおまけにサンプルコード有)
- フレームをどんな感じにするか考える
- RMagickの効果を使って画像を加工してフレームをつくる (必須)
- 合成する (必須)
- YouTubeにアップロードする (必須)
RMagick 以外ではまってたどり着けない可能性を考慮して、RMagickの基本とかその辺説明しつつ・・・
RMagick の基本
↓この元画像を加工しながら、基本を解説
※画像はshutterstockのものを使用。利用規約上この記事外では利用できないので、試すときはスマホで自分の画像撮影する等で適当な画像を用意してください。
エフェクトかける ブラー
require 'RMagick' # require してライブラリを読み込み
img = Magick::ImageList.new("sample.jpg") # 元画像の sample.jpg を読み込み
new_img = img.blur_image(20.0, 10.0) # ブラーエフェクトを適用 戻り値に新しい画像が戻るよ
new_img.write("blur.jpg") # ファイルに書き出し
注意点
メモリの解放
RMagickが取り扱う画像は明示的にメモリ解放の指示destroy!
をする必要がある。たとえば今回の作業のように大量に画像をループ内で作成する場合、画像の書き出しが終わった時点でdestroy!
するのが望ましい
img = Magick::ImageList.new("sample.jpg") # 元画像の sample.jpg を読み込み
new_img = img.blur_image(20.0, 10.0) # ブラーエフェクトを適用 戻り値に新しい画像が戻るよ
new_img.write("blur.jpg") # ファイルに書き出し
new_img.destroy! # ★ここでメモリ解放
画像の拡大縮小
require 'RMagick'
img = Magick::ImageList.new("sample.jpg")
new_img = img.scale(0.25)
new_img.write("small.jpg")
文字入れ
文字入れにはフォントが必要
環境によってはデフォルトのフォントが自動的に選択されるかも・・つまり明示的に指定したほうが無難
今回は、フリーフォントの「源柔ゴシック (げんじゅうゴシック) 」を使用
↑こちらからダウンロード可能
文字入れしてみる
ダウンロードしたフォントファイルの GenJyuuGothic-Normal.ttf を使用
require 'RMagick'
img = Magick::ImageList.new("sample.jpg")
scaled_img = img.scale(300, 300)
font = "GenJyuuGothic-Normal.ttf"
draw = Magick::Draw.new
draw.annotate(scaled_img, 0, 0, 5, 5, '2014年も終わる') do
self.font = font
self.fill = 'blue'
self.stroke = 'transparent'
self.pointsize = 30
self.gravity = Magick::NorthWestGravity
end
scaled_img.write("moji.png") # save to file
見づらいので白で縁取りしてみる
縁取りした場合
require 'RMagick'
img = Magick::ImageList.new("sample.jpg")
scaled_img = img.scale(300, 300)
font = "GenJyuuGothic-Normal.ttf"
draw = Magick::Draw.new
draw.annotate(scaled_img, 0, 0, 5, 5, '2014年も終わる') do
self.font = font
self.fill = 'blue'
self.stroke = 'white'
self.stroke_width = 4
self.pointsize = 30
self.gravity = Magick::NorthWestGravity
end
draw.annotate(scaled_img, 0, 0, 5, 5, '2014年も終わる') do
self.font = font
self.fill = 'blue'
self.stroke = 'transparent'
self.pointsize = 30
self.gravity = Magick::NorthWestGravity
end
scaled_img.write("moji2.png") # save to file
qiita カラー(緑)で
文字色は blue, white とかの組込みの定義済みの名前も使えるし、16進RGB指定も使える
require 'RMagick'
img = Magick::ImageList.new("sample.jpg")
scaled_img = img.scale(300, 300)
font = "GenJyuuGothic-Normal.ttf"
draw = Magick::Draw.new
draw.annotate(scaled_img, 0, 0, 5, 5, '2014年も終わる') do
self.font = font
self.fill = '#428b09'
self.stroke = 'white'
self.stroke_width = 4
self.pointsize = 30
self.gravity = Magick::NorthWestGravity
end
draw.annotate(scaled_img, 0, 0, 5, 5, '2014年も終わる') do
self.font = font
self.fill = '#428b09'
self.stroke = 'transparent'
self.pointsize = 30
self.gravity = Magick::NorthWestGravity
end
scaled_img.write("moji3.png") # save to file
リファレンス
モザイク画像作成(冒頭のやつ)
この記事の頭に貼り付けていた画像もRMagickで作成
require 'RMagick'
require 'yaml'
require "open-uri"
thumbnails = YAML.load(File.open('thumbnails.yml').read)
images = Magick::ImageList.new
thumbnails.each do |uri|
puts uri
images.read(uri) # uri が remote の場合でも、rubyとRMagickどちらも最新版ならこれでもOKのはず。古いとエラー
# urlimage = open(uri) # ruby バージョンが古くてuriがhttpsの場合SSLでエラーが発生する可能性有
# images.from_blob(urlimage.read)
end # thumbnails.each
tile = Magick::ImageList.new
page = Magick::Rectangle.new(0,0,0,0)
images.scene = 0
5.times do |i|
5.times do |j|
tile << images.scale(80, 80)
page.x = j * tile.columns
page.y = i * tile.rows
tile.page = page
(images.scene += 1) rescue images.scene = 0
end
end
mosaic = tile.mosaic
mosaic.write("mosaic.png")
注意点
- バージョン(ruby, RMagick)によって http https のプロトコル使って直接 read できないことがある
- バージョン(ruby, RMagick)によって https の場合SSLの証明書エラーになることがある
以下参考URL
- http://stackoverflow.com/questions/7264895/rmagick-can-not-read-remote-image
- http://railsapps.github.io/openssl-certificate-verify-failed.html
RMagick でアニメーション
この先の作業でのポイント
- 最終的には動画ファイル .mp4
- 動画ファイルの生成には ffmpeg を使用(餅は餅屋で)
- RMagick は動画の元となるフレームの作成に使う
- できるだけ RMagick の機能を使ってフレームをつくる
- 1280x720 ぐらいを目指す
芸術の素養というか、そういうのが無いので、アイデアを拝借。インスピレーションを得る、これ大事。たとえばこういうの。
自分の好きな映画も良いと思う。最近は実写系の映画もオープニング、エンディングで遊んでるから参考にするには良い。印象に残っているのは007とか。ときどき、プログラミングの手を休めて↓のような記事を読んだり、動画をみると新しいアイディアが浮かんでくるかもしれない。
魅力的なアニメーションをつくるための12の原則(動画あり) http://wired.jp/2014/05/14/12-principles-of-animation/
クリエイティブなんて向いてないことを再認識⇒参加者全員が表示される動画に落ち着く
ここまで書いて空腹になったので・・・休憩
gif アニメーション
休憩後、やっぱり、時間無さそう・・・ 動画にしなくてもgifでアニメーションできるし、RMagickのみでつくれるのは良い!
以下参考リンクのみで、ごめんよ
参考リンク
動画でアニメーション
フレームをごりごりつくって、ffmpeg 使って動画にしてもらう。
next で次のフレームに移る処理で、draw で画像ファイルを書きだす感じでいく予定
背景とかフレームの枠を準備
「できるだけ RMagick の機能を使ってフレームをつくる」のでテンプレートとなる画像等は用意しないでフレームをつくっていく。サイズは 1280x720 で。空の画像をRMagickで準備するには Image.new(width, height)
を使う。
require 'RMagick'
f = Magick::Image.new(1280,720) { self.background_color = "white" }
f.write("background.png")
味気ないので、qiita っぽくする。qiita グリーン "#428b09" を拝借。"Ruby Advent Calendar 2014" の文字を入れる。
require 'RMagick'
f = Magick::Image.new(1280,720) { self.background_color = "white" } # 背景を白で準備
draw = Magick::Draw.new
# http://www.imagemagick.org/RMagick/doc/draw.html#rectangle
draw.fill('#428b09') # 先に色を指定して
draw.rectangle(0, 0, 1280, 100) # トップのバナーを長方形で
draw.draw(f) # 塗る
draw = Magick::Draw.new
font = "GenJyuuGothic-Normal.ttf"
draw.annotate(f, 0, 0, 15, 12, 'Ruby Advent Calendar 2014') do
self.font = font
self.fill = 'white'
self.stroke = 'transparent'
self.pointsize = 54
self.gravity = Magick::NorthWestGravity
end
f.write("background.png")
↓これが背景のイメージ
アニメーションに使えそうなRMagickのエフェクト
motion_blur が良さそう。試しに「ス」のアイコンに motion_blur かけて20枚ほどフレーム画像を作成
require 'RMagick'
img = Magick::ImageList.new("src.png")
img.alpha Magick::DeactivateAlphaChannel # アルファチャネルを無効に
img = img.opaque_channel("#000000", "#FFFFFF") # アルファチャネル部分が黒かったので白く
ctr = 0
-10.upto(10) {|i|
if i >= 0
tmp = i != 0 ? img.motion_blur( 0, i*i, 0) : img # 左の方向にぶれる感じ
else
tmp = i != 0 ? img.motion_blur( 0, i*i, 180) : img # 右方向にぶれる感じ
end
file_name = "frames/frame-%03d.png" % ctr
tmp.write(file_name)
ctr += 1
}
21枚の画像ができてこんな感じ
注意点
motion_blur はガウスぼかし gausian_blur を内部的に使用しているため、引数sigma
の値が大きくなるほど計算量が増え処理が遅くなる。使用可能なリソースの範囲でパラメーターを考える必要がある。
みんなのアイコンに使ってみる
require 'RMagick'
# background を準備
bg = Magick::Image.new(1280,720) { self.background_color = "white" } # 背景を白で準備
draw = Magick::Draw.new
# http://www.imagemagick.org/RMagick/doc/draw.html#rectangle
draw.fill('#428b09') # 先に色を指定して
draw.rectangle(0, 0, 1280, 100) # トップのバナーを長方形で
draw.draw(bg) # 塗る
draw = Magick::Draw.new
font = "GenJyuuGothic-Normal.ttf"
draw.annotate(bg, 0, 0, 15, 12, 'Ruby Advent Calendar 2014') do
self.font = font
self.fill = 'white'
self.stroke = 'transparent'
self.pointsize = 54
self.gravity = Magick::NorthWestGravity
end
# ここでエフェクトしたアイコンを張り付けてファイルに出力
base = Magick::Image.new(600,600) { self.background_color = "white" } # 背景を白で準備
overlay = Magick::Image.read("mosaic.png").first # これのサイズは400x400
base.composite!(overlay, 100, 100, Magick::OverCompositeOp) # 中央になるように配置
ctr = 0
-10.upto(10) {|i|
if i >= 0
tmp = i != 0 ? base.motion_blur( 0, i*i*0.5, 0) : base
else
tmp = i != 0 ? base.motion_blur( 0, i*i*0.5, 180) : base
end
file_name = "frames/frame-%03d.png" % ctr
# background の中央に配置
bg.composite!(tmp, 340, 120, Magick::OverCompositeOp) # 中央になるように配置
bg.write(file_name)
ctr += 1
}
同じように21枚の画像ができてこんな感じ
ffmpeg のはなし
複数の画像をつかってアニメーション動画をつくる
1秒間に5枚(5フレーム)の画像を使った動画を作成
ffmpeg -r 5 -i frame-%03d.png -c:v libx264 -pix_fmt yuv420p -y out.mp4
↑こんな感じでコマンドで一発で動画が作成できる!できた動画は↓
※画質が荒いのは元の画像が80x80のため
みんなの顔⇒ https://www.youtube.com/watch?v=AqHmcZ5qplg
使用したオプションの解説
- -r 1秒間のフレーム数を指定する(1より小さい数でも 0.5 とかOK)
- -i 入力に使用するファイル
- -c:v ビデオコーデックの指定
- -pix_fmt ピクセルフォーマットの指定 ※これ指定しないとちゃんとした絵にならない
- -y 同名のファイルがあったら強制的に上書き
動画ファイルに音声(BGM)を加える
- BGMはフリーBGM素材として定評のある「魔王魂」http://maoudamashii.jokersounds.com/list/bgm6.html さんから
- ラスボスっぽい書き手が多いのでラスボスっぽい曲をチョイス
- ffmpeg でできた動画の音声を入れ替え
ffmpeg -i video.mp4 -i bgm.aac -bsf:a aac_adtstoasc -map 0:0 -map 1 -vcodec libx264 -acodec libvo_aacenc -y ruby_advent_calendar_2014.mp4
制作を終えての感想
ヽ(゚ー゚ヽ)(ノ゚ー゚)ノわぁい
ruby を使った意味について・・・
おまけ
使用させていただいたユーザーの皆様
日付順
※12月1日時点(一覧作成中に変更があったりで・・・)
- http://qiita.com/joker1007
- http://qiita.com/awakia
- http://qiita.com/scleen_x_x
- http://qiita.com/tbpgr
- http://qiita.com/igrep
- http://qiita.com/komiyak
- http://qiita.com/michiomochi@github
- http://qiita.com/sonots
- http://qiita.com/kwatch
- http://qiita.com/udzura
- http://qiita.com/shu_0115
- http://qiita.com/tsumekoara
- http://qiita.com/domitry
- http://qiita.com/217
- http://qiita.com/shin_semiya
- http://qiita.com/kwgch
- http://qiita.com/supermomonga
- http://qiita.com/yancya
- http://qiita.com/yuutetu
- http://qiita.com/kwappa
- http://qiita.com/taiyop
- http://qiita.com/yui-knk
- http://qiita.com/hkusu
- http://qiita.com/syuu1228
- http://qiita.com/ocadaruma
qiitaのプロフィール画像
- gravatar の場合もあれば twimg の場合もあって、面倒
- gravatar 系のときプログラムから取得するとエラーになりがち
qiitaのAPI
- ユーザー情報は
http://qiita.com/api/v2/users/joker1007
で取得可能 - Contribution の数字は入らない(Contributionはストックされた合計数だろうか?)
- 投稿数、フォロワー数、フォロー数は取得できる
qiita の Contribution のスクレイピング with Nokogiri
require 'nokogiri'
require 'open-uri'
url = 'http://qiita.com/joker1007'
html = Nokogiri::HTML(open(url), nil, 'utf-8')
ctb = html.xpath('//div[@class="second-line"]/span[@class="count"]/text()')
puts ctb
Advent Caendar から Nokogiri 使ってメッセージを取得
require 'nokogiri'
require 'open-uri'
require 'json'
messages = []
url = "http://qiita.com/advent-calendar/2014/ruby"
html = Nokogiri::HTML(open(url), nil, 'utf-8')
# カレンダーの枠tdを探す
tds = html.xpath('//td[@class="adventCalendar_calendar_day"]')
tds.each do |td|
author = td.xpath('p[@class="adventCalendar_calendar_author"]')
if author.empty? # author 無いなら無視
else
user_id = author.xpath('a/@href').to_s[1, 100] # 先頭スラッシュ無視
puts user_id
message = nil
m_a = td.xpath('p[@class="adventCalendar_calendar_entry"]/a')
if m_a.empty? # 未投稿の場合はこっち
message = td.xpath('p[@class="adventCalendar_calendar_entry"]/text()')
else # 投稿済みの場合
message = td.xpath('p[@class="adventCalendar_calendar_entry"]/a/text()')
end
puts message
messages.push(:user_id=>user_id, :message=>message)
end
end # tds.each
json_file = "messages.json"
File.open(json_file,"w") do |f|
f.write(messages.to_json)
end
qiita API v2 ユーザー情報取得
api/v2/users/:id
{
"description": "Qiita, Qiita:Team(RoR)やKobito(Objective-C)の開発をしています.",
"facebook_id": "yaotti",
"followees_count": 118,
"followers_count": 181,
"github_login_name": "yaotti",
"id": "yaotti",
"items_count": 101,
"linkedin_id": "yaotti",
"location": "Tokyo, Japan",
"name": "Hiroshige Umino",
"organization": "Increments Inc",
"profile_image_url": "https://si0.twimg.com/profile_images/2309761038/1ijg13pfs0dg84sk2y0h_normal.jpeg",
"twitter_screen_name": "yaotti",
"website_url": "http://yaotti.hatenablog.com"
}
Gravatar のAPI
gravatar なんて使っている人少ないと思ってたのだがけっこういるのね。メールアドレスをハッシュしたこんな感じのURLで画像を取得できます。パラメーターs
でサイズ指定できるのがポイント
http://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200
ruby + RMagick でやった意味について
- やったことはシェルで ImageMagick + ffmpeg 使うのと変わらないではないか
- はいその通りです
- ruby でやる意味はどこにあったのか
- RMagick 周辺を綺麗にラップした感じのものをつくれたら意味あるかも ※RMagickが処理するエフェクトメソッドの内、self戻すのもあれば、new image戻すものもあったりとややこしいから
- 何が得られたのか
- 作業中はCPU使用率が100%近くを維持できた