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

goimagicksyokai

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

 goimagicksyokai

GoImagick詳解
(GoCon2016Spring用)

Avatar for yoya

yoya

April 22, 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  でバイナリ弄るのが持ちネタ   •  Golang  は触り始めて一年ちょっと ?
  2. もくじ •  GoImagick  とは   •  セッティング   •  使い方

      •  内部の話   •  最近のトピック   •  蛇足   •  まとめ  
  3. GoImagick とは •  hAps://github.com/gographics/imagick   •  MagickWand  API  (C言語)の Go

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

     バインディング   •  GoImagick は MagickCore の定義を取り入れ るけど、関数は MagickWand  だけ橋渡し   間違い
  5. MagickWand と MagickCore •  GoImagick は MagickWand の関数を使う ImageMagick  

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

      – convert  コマンド   •  wand/mogrify.c   – PHP  imagick   •  hAp://php.net/manual/ja/imagick.transformimage.php   似た名前と引数の   メソッドが GoImagick  に   もあると期待出来る
  7. GoImagick  セッティング  (MacOS)  (1/2) •  少し前まで master      

      •  今のやり方 $  sudo  port  install  ImageMagick          #  ImageMagick  v6.8.9-­‐9以降   $  go  get  gopkg.in/gographics/imagick.v2/imagick   $  sudo  port  install  ImageMagick      #  古いやり方   $  go  get  github.com/gographics/imagick   以下のエラーが出る   expects  import  "gopkg.in/gographics/imagick.v2/imagick”  
  8. GoImagick  セッティング  (MacOS)  (2/2) •  ImageMagick  を brew  と  ports

     両方とも入れて る場合         •  pkg-­‐config  の実行パスを変えればOK $  PATH=/opt/local/bin:$PATH  go  install  gopkg.in/gographics/imagick.v2/imagick   /*   #cgo  !no_pkgconfig  pkg-­‐config:  MagickWand  MagickCore   */   import  "C"   リンク先は   pkgconfig  コマンドで決める
  9. GoImagick  セッティング  (CentOS) •  RPM  を使う場合        

    •  ImageMagick  を自分でbuildする場合 $  (cd  ImageMagick-­‐6.9.3-­‐8  ;  ./configure  ;  make  install)          #  ImageMagick  v6.8.9-­‐9以降  (最近の ImageMagick  はこっち)   $  go  get  gopkg.in/gographics/imagick.v2/imagick   $  sudo  yum  install  ImageMagick-­‐devel          #  ImageMagick  v6.8.9-­‐8以前 (dpkg  も多分これ)   $  go  get  gopkg.in/gographics/imagick.v1/imagick  
  10. GoImagick の使い方 (1/3) •  例えば、640x480  にリサイズする   ( _  =  〜はエラーの値。本来はチェックするべき)

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

       $  go  run  resize640x480.go  gopher.png   250px 340px 480px 640px ふくよかな   Gopher!
  12. GoImagick のファイル •  ディレクトリ構成  >  imagick  と example  の2つ  

    – 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  本体
  13. 動作環境の構築(初期化) •  ImageMagick  が動作する環境を用意する MagickCore  (ImageMagick  のコア部)   <module名>Genesis  

    (セマフォ用意) MagickWandGenesis 環境変数   取り込み SIG〜ハンドラ   設定 MagickCoreGenesis
  14. ImageMagick の動作環境生成 •  MagickWandGenesis  を呼ぶだけ   •  imagick/magick_wand_env.go //  IniRalizes

     the  MagickWand  environment   func  IniRalize()  {                  envSemaphore  <-­‐  struct{}{}    defer  func()  {                                  <-­‐envSemaphore                  }()      initOnce.Do(func()  {                                  C.MagickWandGenesis()                                  terminateOnce  =  &sync.Once{}      setCanTerminate()                  })   }   Cの関数呼ぶの   簡単ですね!   /*   #include  <wand/MagickWand.h>   */   import  "C"   Cの関数を呼ぶ   のに必要  
  15. ImageMagick の動作環境廃棄 •  MagickWandTerminus  を呼ぶだけ   •  Imagick/magick_wand_env.go   func

     Terminate()  {                  envSemaphore  <-­‐  struct{}{}                  defer  func()  {                                  <-­‐envSemaphore                  }()                    if  terminateOnce  !=  nil  {                                  terminateOnce.Do(func()  {                                                  runRme.GC()                                                  terminate()                                  })                  }   }   func  terminate()  {    <-­‐canTerminate                  fmt.Println("C.MagickWandTerminus()")                  C.MagickWandTerminus()                  initOnce  =  sync.Once{}   }  
  16. ユーザはどうすれば良い? •  プログラムのはじめに一度だけ呼べば良い   •  ライブラリとして用意する場合は別   – ライブラリの機能を使い終わったら imagick.Terminate()  で動作環境を消せるようにし

    た方がコンピュータに優しい   var  imagick_iniRalized  =  func()  bool  {                  imagick.IniRalize()                  return  true   }()  
  17. MagickWand  の登場人物 •  3つのオブジェクトを介して操作する MagickWandAPI   MagickWand   (画像) DrawingWand

      (描画) PixelWand   (ピクセル操作) mw  :=  imagick.NewMagickWand()   _  =  mw.ReadImage(”input.png”)   _  =  mw.ResizeImage(640,  480,  imagick.FILTER_UNDEFINED,  1)   _  =  mw.WriteImage("output.png”)   リサイズや   合成だけなら、   これだけでOK 文字入れは   これらが必須 文字入れは   これらが必須
  18. MagickWand オブジェクト  (1/1) •  C.NewMagickWand  を呼んでカプセル化   •  Imagick/magick_wand.go  

    type  MagickWand  struct  {                  mw      *C.MagickWand                  init  sync.Once   }     func  newMagickWand(cmw  *C.MagickWand)  *MagickWand  {                  mw  :=  &MagickWand{mw:  cmw}                  runRme.SetFinalizer(mw,  Destroy)                  mw.IncreaseCount()                    return  mw   }   func  NewMagickWand()  *MagickWand  {                  return  newMagickWand(C.NewMagickWand())   }   GC  に回収   タイミングを   任せる   MagickWand  が   1つでもあるうちは   Terminus  を   動かさない  
  19. MagickWand オブジェクト  (2/1) •  Destroy   •  Imagick/magick_wand.go   func

     (mw  *MagickWand)  Destroy()  {                  if  mw.mw  ==  nil  {                                  return                  }                    mw.init.Do(func()  {                                  mw.mw  =  C.DestroyMagickWand(mw.mw)                                  relinquishMemory(unsafe.Pointer(mw.mw))                                  mw.mw  =  nil                                    mw.DecreaseCount()                  })   }   DestroyMagickWand  を呼ぶだけ   多分無駄だけど   念のため   MagickWand  が全て片付くまで   Terminus  は呼ばせない  
  20. MagickWand オブジェクト  (3/1) •  ReadImage   •  Imagick/magick_wand_image.go   func

     (mw  *MagickWand)  ReadImage(filename  string)  error  {                  csfilename  :=  C.CString(filename)                  defer  C.free(unsafe.Pointer(csfilename))                  ok  :=  C.MagickReadImage(mw.mw,  csfilename)                  return  mw.getLastErrorIfFailed(ok)   }   func  (mw  *MagickWand)  getLastErrorIfFailed(ok  C.MagickBooleanType)  error  {                  if  C.int(ok)  ==  0  {                                  return  mw.GetLastError()                  }  else  {                                  return  nil                  }   }   エラーは   GetLastError  で取得   メソッドを生やす   Go の文字列を   Cの文字列にする   Cの関数に渡す  
  21. 最近のトピック •  MagickWand  等の回収を  GC  に任せたい   – Make  mw,  pi,

     pw,  dw  objects  destroyable  in  GO   GC  #62   •   hAps://github.com/gographics/imagick/pull/62   •  メモリリークをたくさん修正 (by  yoya)   – fixed  to  memory  leak,  string  array  issue.   •  hAps://github.com/gographics/imagick/pull/37   •  hAps://github.com/gographics/imagick/pull/39  
  22. GC  に任せる(1/3) •  以前は MagickWand  をこういう使い方してた   •  いちいち defer

     Destroy  するの面倒だよね?   func  main()  {                  imagick.IniRalize()                  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")   }  
  23. GCに任せる(2/3) •  defer  で destroy  する問題点 (一般論)   – 毎回  defer

     書くのは面倒だし忘れるかも?   – 関数の外に  return  出来ない   – New〜  じゃないのに新しく作った MagickWand  を 返すメソッドもあって漏れがち   •  CropImage   •  TransformImage   •  これらの戻り値も Destroy  しないとリークする  
  24. GC  に任せる(3/3) •  runRme.SetFinalizer  を使う   •  GC  対象になると SetFinalizer

     で指定したメソッ ド(Destroy)が呼ばれる   func  newMagickWand(mw  *C.MagickWand)  *MagickWand  {    mw  :=  &MagickWand{mw:  cmw}    runRme.SetFinalizer(mw,  Destroy)    mw.IncreaseCount()    return  mw   }   DONE!
  25.  メモリ管理を色々と修正 (1/3) •  フォント名一覧取得でメモリリークしたので調査   •  同様のメモリリークがあちこちにあった   –  文字列のリストを取得する系メソッドが大体ダメ

      –  ImageMagick  (MagickCore/MagickWand)  の中で AcquireMagickMemory(ImageMagick  のメモリ管理)で 取得したメモリを、標準の free  で解放してた   •  RelinquishMemory  を使うべき  
  26. まとめ •  GoImagick  は ImageMagick  Wand  API  のラッパー   • 

    import  先が最近変わったので注意   •  IniRlize をはじめに一度だけ実行する   –  MagickWand で画像を処理する   –  DrawingWand  で文字を描画する   –  PixelWand  で文字の色を指定する   •  MagickWand  等の回収は  GC  時に勝手にやってくれる事になった よ!   •  これで、GoImagick  で何かあっても自分でデバッグ出来るはず!   Let’s  Try!