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

goimagicksyokai

yoya
April 22, 2016

 goimagicksyokai

GoImagick詳解
(GoCon2016Spring用)

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!