Slide 1

Slide 1 text

アプリケーションの 開発運用で 当たり前に必要とされる 画像変換の中身 pixiv Inc. 2019.3.23 RailsDM2019

Slide 2

Slide 2 text

2 自己紹介 ● CRubyのコミッタ (安定版リリースマネージャ ・Windows版担当) ● www.ruby-lang.orgの記事をいっぱい書いてる人 (全体の1/4くらい) ● 配信技術部 = 画像とか動画とかそのものを取り 扱ってインターネットに流すための諸々をやるところ ● 仕事では普段はだいたいGoで書いている ● 仕事以外では普段はだいたいCで書いている 配信技術部エンジニア

Slide 3

Slide 3 text

福岡 3

Slide 4

Slide 4 text

4/12(金) 19:00~ ※RubyKaigiの前週末 参加申し込み:https://pixiv.connpass.com/event/124727/ 登壇申し込み:https://goo.gl/forms/V2rJnL47vad4XDSg2 4

Slide 5

Slide 5 text

アプリケーションの 開発運用で 当たり前に必要とされる 画像変換の中身 pixiv Inc. 2019.3.23 RailsDM2019

Slide 6

Slide 6 text

アプリで画像、使うよね? 6 ● デザイン上必要な固定の画像はまあいい ○ 枠とかの装飾っぽい奴とか ○ 端の方に置くロゴとか ● そうじゃなくて、コンテンツごとに貼りつけるやつが問題 ○ いっぱいある ○ 縮小表示しておいてクリックしたらでっかくなるように、とか

Slide 7

Slide 7 text

7 「 アプリ」は だけじゃないよ 7 ● 実質ブラウザっていうスマートフォン用アプリ ○ 皆さん作ってますよね? ● 「実質ブラウザ」じゃないにしても、コンテンツを HTTP(S)で随時拾ってくる系のやつ ○ ゲームとか ○ ショッピングサービス(EC)

Slide 8

Slide 8 text

で、その アプリで、 画像、 どうしてる? 8

Slide 9

Slide 9 text

9 :なにもしない 9 ● ● クライアント側の負荷 ● 転送量膨大 ● 速度低下 ● UXの悪化

Slide 10

Slide 10 text

10 :手元で必要な加工をしてアップロード デプロイ 10 ● 心温まる手作業…… ● めんどくさい ● だいたい定型作業なので頻度が増えてきたらスクリプトである程度自動化 ○ あれ、自動化してるのになんで人間が手で叩いてるんだっけ ……? ● そもそも、画像をアップロードするのが自分 (開発・運用者)じゃない場合がある ○ ユーザーがアップロードするやつとか

Slide 11

Slide 11 text

11 :アップロード時にサーバサイドで加工 11 ● 楽ちん! ● 配信サーバと共有すると運用上いろいろ不安がなくもない ○ 負荷(特にメモリ)がアップロード時のみ高まる ■ 金で解決してもいいけど、普段は完全にオーバースペックで無駄 ○ き、脆弱性…… ● 分離した画像変換サーバを用意すればいくらかマシにはなる

Slide 12

Slide 12 text

● アップロードをトリガにして、所定の 加工を行う ● 生成した画像を、クライアントがアクセスできるところに置く ● おわり ○ こういうのの作り方は、ぼくより皆さんの方が詳しい 12 画像変換サーバの概要

Slide 13

Slide 13 text

● 多種多様な画像フォーマットに対応 ○ 画像どころかPDFとかまで対応しちゃう ● 多彩な変換に対応 ○ ぶっちゃけ、単純な図形の組み合わせ画像とかならこれだけで作れる ● つまり、つよい! べんり!! 13

Slide 14

Slide 14 text

● いいことばかりではない……(残念……) ● 依存ライブラリが異様に多い(対応画像フォーマットの数が問題 ) ○ バージョン管理とか脆弱性対応が大変 ● 各種画像フォーマットに対応した上でありとあらゆる加工を実現するため、いったんメモリ 上で中間形式(RGB(+Alpha)、各要素float)に展開している ○ 死ぬほどメモリ食う ○ 速度イマイチ 14

Slide 15

Slide 15 text

● ブラウザが対応してる程度の画像フォーマットだけでええねん ○ JPEG, PNG, GIF, あとせいぜいWebPくらい ● ぶっちゃけ、加工といっても、縮小だけできればええねん ○ だってサムネイル画像作りたかっただけだし 15 顧客 あなた が本当に欲しかったもの

Slide 16

Slide 16 text

あれ? これくらいなら 自分で 作れるんじゃね? 16

Slide 17

Slide 17 text

ほんとうに??? 17

Slide 18

Slide 18 text

実際に 画像変換エンジンを  作ってみよう! 18

Slide 19

Slide 19 text

● Go ○ Rubyだと思った? ごめんね ● 単体で動くバイナリが生成されるのでデプロイが簡単で便利 ● imageっていうパッケージがあって、JPEG, PNG, GIFとか読み書きできる ○ 今回の用途にはぴったり! 19 用意するもの

Slide 20

Slide 20 text

20 画像ファイルの読み込み import( "image" "os" ) func LoadImage(path string) (image.Image, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() img, _, err := image.Decode(file) return img, err }

Slide 21

Slide 21 text

21 画像ファイルの書き出し func SaveImage(img image.Image, path string) error { file, err := os.Create(path) if err != nil { return err } defer file.Close() switch filepath.Ext(path) { case ".jpg", ".jpeg": return jpeg.Encode(file, img, &jpeg.Options{Quality: 100}) case ".png": return png.Encode(file, img) case ".gif": return gif.Encode(file, img, &gif.Options{}) } return fmt.Errorf("unsupported file: %s", path) }

Slide 22

Slide 22 text

● bilinearなり、bicubicなり、Lanczosなり、お好みの方法で ○ どんなアルゴリズムでやるとしても、結局、実際の画素データを触らないとリサイズで きない。それはそう ● image.Image#At(x, y) で指定座標の画素データが得られる ○ color.Color 型 ○ なにこれ? 22 リサイズ

Slide 23

Slide 23 text

● 画素が示す色を抽象化した型 ● その実態は様々 ○ color.RGBA (color.RGBA64), color.NRGBA (color.NRGBA64) ○ color.Gray (color.Gray16) ○ color.Palette ○ color.YCbCr ○ color.YMCK 23 型

Slide 24

Slide 24 text

● AはAlpha channel(透過度、というか、透過してない度 ) ● Nは何? ● みんながぱっと想像する単純な R, G, B, Aの各値を持つのはNRGBA ○ RGBAはR, G, BにAlphaの値を掛けたものが入っている(alpha-premultiplied) ○ なので、NRGBAは “Non-alpha-premultiplied RGBA” のこと ● どうせ表示する時は掛け算しないといけないので、最初から掛けておくという発想 (クライ アント側に優しい……のかなぁ……) 24 と

Slide 25

Slide 25 text

● 0 (透過100%)をR, G, Bに掛けると、全部 0 になる ○ つまり色情報が消える ● リサイズ時に問題が起きる ○ 隣のピクセルの色を見て色を混ぜたりする ○ 透過ピクセルの色は常に「黒」 ● color.Color 型の共通インターフェースは RGBA() ○ 実際のデータがNRGBAであっても直接メンバを見ない限り RGBAになる 25 の落とし穴

Slide 26

Slide 26 text

● 頑張るしかないので頑張る ● PaletteはどうせRGB(+A)に変換するしかないのでそうする ● Grayは、メモリ消費を抑えたいならそのまま ○ 実装コストとのトレードオフ 26

Slide 27

Slide 27 text

● 知ってる? ○ Y:輝度 ○ Cb:青色差 ○ Cr:赤色差 ● 人間の視覚は輝度の変化には敏感だがそれと比べれば色差には鈍感 ○ なので輝度情報と比べて色差の情報は間引ける ● RGBとは簡単な計算式で相互変換可能 27

Slide 28

Slide 28 text

● JPEGはだいたいYCbCr ○ 内部的にRGBへの変換を経由しない方が、速度的にもメモリ的にもお得 ○ いろんな規格で微妙に変換式中の定数が違うので RGB経由自体が危険 ● ビット表現も1種類じゃない(Goのimage.YCbCr型は差異を吸収) ○ 4:4:4 ○ 4:2:2, 4:1:1 ○ 4:2:0, 4:1:0 28

Slide 29

Slide 29 text

● 印刷用 ● Cyan, Magenta, Yellow, K...??? ○ blacK? Kuro? Karbon? Key plate? ● 入稿用データとかでは普通に存在する ● これも加工時にはRGBを経由しない方が無難 29

Slide 30

Slide 30 text

● いろんなメタ情報が入ってる ○ 撮影場所のGPSの情報とかが入っていて問題になりがち ● Go標準の image/jpeg パッケージはExifを取り扱わない ○ ので読んで書いたら消える。ラッキー!? ● Orientation情報:回転 ○ 消したら「画像の向きが変わった!」とか言われる ● Exif読み込んで解釈してGPS情報だけ消すとか地獄 30 問題

Slide 31

Slide 31 text

● ICC:International Color Consotium ● 追加の色情報(色空間の補正) ● 最近のだいたいのブラウザはサポートしている ● ブラウザと画像処理専門ソフト(Photoshopとか)以外はサポートしてない ● Goの image パッケージも未サポート ○ 読んで書いたら消える ○ つまり、色が変わって見える 31 プロファイル問題 https://en.wikipedia.org/wiki/Color_space より

Slide 32

Slide 32 text

結論 32

Slide 33

Slide 33 text

● ここまで挙げた以外にも落とし穴はいっぱいある ○ 各種の画像フォーマット、その派生規格、画像処理、最適化、 etc… にそこそこ精通す る必要がある ● 我々が本当に作りたかったのは Webアプリケーションであって画像変換エンジンじゃない ○ なにかしらがないと顧客に価値(UX)を届けることはできないが、そのために画像変換 エンジンを自作するのはコストが高すぎる 33 生半可な覚悟で画像変換エンジンは作れない

Slide 34

Slide 34 text

は偉大 がんばって うまく使いこなそう 34

Slide 35

Slide 35 text

もうちょっとだけ 続くんぢゃ 35

Slide 36

Slide 36 text

36 :アップロード時にサーバサイドで加工 続き 36 ● デザイナー < あ、デザイン変えたんでよろしく (^^) ○ 全画像ファイル再生成 ● Ap〇le < あ、新機種で解像度変えたんでよろしく (^^) ○ 全画像ファイル再生成(※旧機種用も残す必要あり)

Slide 37

Slide 37 text

37 :オンデマンドでアクセスされたときに 加工後画像生成 37 ● キャッシュヒット率が十分高ければ現実的選択肢 ● 運用上の困難は増大 ○ 脆弱性対応 ○ アクセススパイク時の対応 ○ アラート、アラート、アラート……

Slide 38

Slide 38 text

注: ここからは宣伝です 38

Slide 39

Slide 39 text

39 :画像変換 にまかせる 39 ● オンデマンド変換なので必要なときに必要な画像を生成 ● 運用上の困難はSaaS側に押し付けられる ● 最近はCDN屋さんがいろいろその手の付加サービスを提供し始めた

Slide 40

Slide 40 text

40

Slide 41

Slide 41 text

● 世界最大級のイラスト共有SNS「pixiv」で培った画像変換のノウハウを投入 ○ 同じものが今もpixivをはじめとするピクシブの各サービスで使われている ● インフラはさくらインターネットが提供 ○ アクセスポイントは国内にあるので高速 (太平洋往復したりしない) ● URLに変換パラメータを含めるだけで画像を変換するプロクシとして機能 ○ HTMLさえ書ければ、デザイナーが新デザイン案を試してみる、とかいう事も可能 41 の特徴

Slide 42

Slide 42 text

42 の使用例

Slide 43

Slide 43 text

● image_flux.gemというものがあります ○ 宇宙海賊さん作 ○ https://github.com/space-pirates-llc/image_flux 43 コード絶無は悲しいので、ご紹介 require ”image_flux” origin = ImageFlux::Origin.new(domain: ”demo.imageflux.jp”) origin.image_url(”/bridge.jpg”, width: 600) #=> ”https://demo.imageflux.jp/c/w=600/bridge.jpg”

Slide 44

Slide 44 text

それはそれとして 44

Slide 45

Slide 45 text

弊社と契約して 画像変換エンジニアに なってほしいんだ! 45

Slide 46

Slide 46 text

● ぼくと一緒に画像変換サービスを作ろうよ! ○ 罠と課題はまだまだ無限にある ○ 最強の画像変換SaaSを実現しよう! ● 最近は動画変換サービス ImageFlux Live Streaming も開始 ○ こちらの技術者ももちろん募集中 ● https://recruit.jobcan.jp/pixiv/show/b001/2969 46 画像変換サービス開発たのしす