Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GoImagickThumbnail

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for yoya yoya
April 08, 2016

 GoImagickThumbnail

GoImagickでサムネール作成

Avatar for yoya

yoya

April 08, 2016
Tweet

More Decks by yoya

Other Decks in Programming

Transcript

  1. 自己紹介 (@yoya) •  プロファイル   – hAps://osdn.jp/users/yoya/   •  ImageMagick  のストーカーしてます

      – hAp://d.hatena.ne.jp/yoya/searchdiary? word=ImageMagick   •  昔、PHP  でバイナリを弄ってました   – hAps://github.com/yoya/IO_MIDI   – hAps://github.com/yoya/IO_JPEG   •  Golang  は触り始めて一年ちょっと ?
  2. GoImagick とは •  hAps://github.com/gographics/imagick   •  MagickWand  API  (C言語)の Go

     バインディング   –  Go  言語で ImageMagick  の機能が使えます  
  3. 公式サイトからもリンク •  hAp://imagemagick.org/script/api.php#go     •  MagickWand  と MagickCore  の

    Go  バインディ ング   – GoImagick はMagickCore の定義を取り入れるけ ど、関数は MagickWand  のだけ使います。  
  4. MagickWand と MagickCore •  GoImagick は MagickWand の関数を使う ImageMagick  

    MagickCore   (magick) coders MagickWand   (wand) uWliWes   画像処理   本体は   ココ PerlMagick PHP  imagick convert   コマンドはここ 使い易くする   為のAPI GoImagick JPEGやPNG   の入出力
  5. つまり? •  convert  コマンドと PHP  imagick  のコードを見 ると GoImagick  の使い方も分かる

      – convert  コマンド   •  wand/mogrify.c   – PHP  imagick   •  hAp://php.net/manual/ja/imagick.transformimage.php  
  6. なぜ ImageMagick  を使うのか? •  Golang  標準で image  パッケージあるよね?   – 機能少ないし

    対応形式は JPEG,GIF,PNG  だけ   •  libpng  や jpeglib  を直接使わないの?   – go-­‐thumber  がそうだけど cgo  は難易度高い     •  他にも画像変換ツールがあるのでは?   – ImageMagick  は困った時に検索で探しやすい   •  (恐らく人による。自分は ImageMagick  が楽)  
  7. ImageMagick  を使う理由(2/2) •  メジャーな画像フォーマットからマイナーなものま で100種類以上に対応してる   •  hAp://www.imagemagick.org/script/formats.php png jpeg

    gif inline sixel webp svg pdf 超メジャー 最近の キワモノ系   (Webの  base64画像とか) ベクター画像 dcm 医療系   (DICOM等)
  8. GoImagick 導入 (MacOS編) •  少し前まで   •  今のやり方 $  sudo

     port  install  ImageMagick   $  go  get  github.com/gographics/imagick            #  ImageMagick  v6.8.8以前 (rpm や dpkg  とかで古い場合)   $  go  get  gopkg.in/gographics/imagick.v1/imagick          #  ImageMagick  v6.8.9以降 (macports  や最新版を使う場合)   $  go  get  gopkg.in/gographics/imagick.v2/imagick   以下のエラーが出ます   expects  import  "gopkg.in/gographics/imagick.v2/imagick"
  9. GoImagick 使用例 •  resize640x480.go   _  =  〜はエラーの値。ちゃんと拾って処理すべき   package

     main   import  (                  "gopkg.in/gographics/imagick.v2/imagick”   )   func  main()  {                  imagick.IniWalize()                  defer  imagick.Terminate()                  mw  :=  imagick.NewMagickWand()                  defer  mw.Destroy()                  _  =  mw.ReadImage(”input.png”)                  _  =  mw.ResizeImage(640,  480,  imagick.FILTER_UNDEFINED,  1)                  _  =  mw.WriteImage("output.png")   }  
  10. ResizeImage(640,480,…  実行   •  縦横のアスペクト比が…   – サムネール画像としてはNG      

     $  go  run  resize640x480.go  gopher.png   250px 340px 480px 640px ふくよかな   Gopher!
  11. アスペクト比を保つ方法(1/2) •  出力サイズを変えてしまう   –  はみ出ないように (内接)   •  480

     ×  (250/340)   •  =>  352x480   •  ResizeImage(352,480,…   –  減らさない  (外接)   •  640  ×  (340/250)     •  =>  640x870   •  ResizeImage(640,870,…   •  ResizeImage  だけで良い       250px 340px 480px 870px 640px 640px 352px 480px
  12. アスペクト比を保つ方法(2/2) •  出力サイズを変えない   – マージンをつける (内接)   •  480  ×

     (250/340)   •  =>  352x480   – クロップする  (外接)   •  640  ×  (340/250)     •  =>  640x870   •  ResizeImage  だけでは無理       250px 340px 480px 870px 640px 640px 352px 480px
  13. マージン(内接)の方法 •  描画領域を広げる (ExtentImage)   – (640  –  352)  /  2)

     =  144  ⇦  左右に144拡げる   480px 640px 144px 640px 352px 480px 480px 352px _  =  mw.ResizeImage(352,  480,  imagick.FILTER_UNDEFINED,  1)   _  =  mw.ExtentImage(-­‐144,  0,  640,  480)  //  -­‐extents   _  =  mw.ResetImagePage(“”)                                        //  +repage  
  14. クロップ(外接)の方法(1/2) •  描画領域を削る (ExtentImage)   – (640  –  352)  /  2)

     =  144   _  =  mw.ResizeImage(640,  870,  imagick.FILTER_UNDEFINED,  1)   mw2  =  mw.CropImage(0,  0,  640,  480)  //  -­‐crop   defer  mw2.Destory()   870px 640px 640px 480px 480px
  15. クロップ(外接)の方法(2/2) •  リサイズとクロップ同時 (TransformImage)   – 250  ×  (480/640)  =  187.5

      crop_src  :=  “250x187+0+0”   geom_dst  :=  “640x480”   mw2  =  mw.TransformImage(crop_src,  geom_dst)   defer  mw2.Destroy()   640px 480px 250px 340px 187px
  16. 画像合成   •  CompositeImage で合成できる _  =  mw1.ReadImage(“gopher.png”)   _

     =  mw2.ReadImage(“blind.png”)   _  =  mw1.CompositeImage(mw2,  imagick.COMPOSITE_OP_OVER,  45,  28)   CompositeImage gopher.png blind.png
  17. 文字入れ  (1/5)   •  DrawingWand  と PixelWand  を使う   – DrawingWand

     でフォントを指定   •   (日本語を表示するなら必須)         – (蛇足)  QueryFont  で扱えるフォントが分かる dw  :=  imagick.NewDrawingWand()   defer  dw.Destroy()   _  =  dw.SetFont("Noto-­‐Sans-­‐CJK-­‐JP-­‐Medium”)   dw.SetFontSize(24)   fonts  :=  mw.QueryFont(“*”)   fmt.Prinv(“%#v”,  fonts)  
  18. 文字入れ  (2/5)   •  PixelWand で色を表現   •  DrawingWand  で色と文字を設定する

            pw  :=  imagick.NewPixelWand()   defer  pw.Destroy()   _  =  pw.SetColor(”rgb(0,  0,  0)”)   dw.SetFillColor(pw)   dw.AnnotaWon(0,  0,  “Gopher!!”)  
  19. 文字入れ  (3/5)   •  DrawingWand  の文字を MagickWand  の画像に 描画  

    •  (0,0)を基準に文字を貼るので殆ど見えない   –  見えてるのは p  の下にはみ出た部分   •  Gravity 方式で配置しよう _  =  mw.DrawImage(dw)   あれれ?
  20. 文字入れ  (4/5)   •  CENTER 指定と SOUTH  指定 dw.setGravity(imagick.GRAVITY_CENTER)  

    dw.AnnotaWon(0,  0,  “Gopher!!!”)   mw.DrawImage(dw)   GRAVITY_SOUTH  
  21. 文字入れ  (5/5)   •  まとめ imagick.IniWalize()   defer  imagick.Terminate()  

    mw  :=  imagick.NewMagickWand()   defer  mw.Destroy()   dw  :=  imagick.NewDrawingWand()   defer  dw.Destroy()   pw  :=  imagick.NewPixelWand()   defer  pw.Destroy()   _  =  mw.ReadImage(os.Args[1])   _  =  dw.SetFont("Noto-­‐Sans-­‐CJK-­‐JP-­‐Medium")   dw.SetFontSize(24)   _  =  pw.SetColor("rgb(255,  0,  0)")   dw.SetFillColor(pw)   dw.SetGravity(imagick.GRAVITY_CENTER)   dw.AnnotaWon(0,  0,  "Gopher!!!")   _  =  mw.DrawImage(dw)   _  =  mw.WriteImage("output.png”)  
  22. MagickWand  の注意点 •  メソッドが MagickWand  を返した時にも Destroy  が必要   – 中で

    new  相当の処理が動いてる crop_src  :=  “250x187+0+0”   geom_dst  :=  “640x480”   mw2  =  mw.TransformImage(crop_src,  geom_dst)   defer  mw2.Destroy()   _  =  mw.ResizeImage(640,  870,  imagick.FILTER_UNDEFINED,  1)   mw2  =  mw.CropImage(0,  0,  640,  480)  //  -­‐crop   defer  mw2.Destory()  
  23. GoImagick の中身 •  使うだけでなく中身も見よう   – hAps://github.com/gographics/imagick   imagick$  ls  

    CREDITS    LICENSE    env.sh    imagick   History.md  README.md  examples   imagick$  ls  imagick  |  wc              73            73        1298   imagick$  ls  -­‐R  examples    |  wc              92            71          874   サンプルが沢山ある GoImagick  本体
  24. ところで •  hAp://imagemagick.org/script/api.php#go   •  なぜ GoImagick  と ImageMagick本家で説明 に食い違いがあるのか。

      •  なぜ GoImagick  と ImageMagick本家で説明 に食い違いがあるのか   – GoImagick  ⇨  MagickWand   – ImageMagick本家 ⇨  MagickWand  +  MagickCore
  25. MagickWand と MagickCore •  おおまかな構造 ImageMagick   MagickCore   (magick)

    coders MagickWand   (wand) uWliWes   画像処理   本体は   ココ PerlMagick PHP  imagick convert   コマンドはここ 使い易くする   為のAPI GoImagick ??? JPEGやPNG   の入出力
  26. つまりどういう事? •  利用する関数は MagickWand だけ   – MagickCore  ではない (型の取り込みで include

     し てるだけ)   •  PerlMagick  より PHP  imagick  のサンプルが参 考になるという事
  27. 最近のトピック •  メモリ管理を色々と修正 (by  yoya)   –  fixed  to  memory

     leak,  string  array  issue.   •  hAps://github.com/gographics/imagick/pull/37   •  hAps://github.com/gographics/imagick/pull/39   •  Magick.IniWalize()  に Mutex  をかけたい(協議中)   –  Fix  IniWalize/Terminate  race  condiWon  #43   •  hAps://github.com/gographics/imagick/pull/43   •  MagickWand  等の回収を  GC  に任せたい   –  Make  mw,  pi,  pw,  dw  objects  destroyable  in  GO  GC  #62   •   hAps://github.com/gographics/imagick/pull/62  
  28.  メモリ管理を色々と修正 (1/3) •  フォント名一覧取得でメモリリークした   –  似たような漏れが他にもあるのでは?   –  Malloc

     してる箇所が見当たらないのに、free  してるけ どそのポインタは大丈夫なの?   •  関連するバグを調査   –  似たようなリークがあちこちにあった   •  文字列のリストを取得する系メソッドが大体ダメ   –  Wand  API  の中で AcquireMagickMemory(ImageMagick  のメモリ管理)で 取得したメモリを、標準の free  で解放してた   •  RelinquishMemory  を使うべき  
  29. Magick.IniWalize()  に Mutex •  Magick.IniWalize()  や Terminate()  をマルチス レッドで呼ぶと競合するので Mutex

     をかけた い   •  Sync.once  で  IniWalize  を一度だけ呼べばよく ない?   •  でもユーザに気をつけろというより仕組みを 入れた方がよくない?   •  協議続行中
  30. GC  に任せる(1/2) •  runWme.SetFinalizer  を使う   •  GC  対象になると SetFinalizer

     で指定したメソッ ド(Destroy)が呼ばれる   func  newMagickWand(mw  *C.MagickWand)  *MagickWand  {    mw  :=  &MagickWand{mw:  cmw}    runWme.SetFinalizer(mw,  Destroy)    mw.IncreaseCount()    return  mw   }  
  31. GCに任せる(2/2) •  defer  で destroy  する問題点 (一般論)   – 毎回  defer

     書くのは面倒だし忘れたりする   – 関数の外に  return  出来ない   – 明示的に new  するものはまだ良いけど、新しく MagickWand  を返すメソッドもあって漏れがち   •  CropImage   •  TransformImage   •  これらの戻り値も Destroy  しないとリークする   •  defer  mw.Destroy  といちいち書かなくてもよく なると嬉しい!  
  32. まとめ •  MagickWand で画像を処理する   •  DrawingWand  で文字を描画する   • 

    PixelWand  で文字の色を指定する   •  defer  mw.Destroy()  を忘れずに   –  CropImage  や TransformImage  が返すのも Destroy()  をお 忘れずに   •  ここまでの話を聞けば、Golang  でサムネール画像を 作れるはず   –  Let’s  try!