画像の背景色の除去

問題

物体画像のデータベースなどから得た画像は、多くの場合に物体の周囲に白い背景が付いています。実験では黒や灰色の画面に画像を提示することが多いので、画像をそのまま提示すると物体の周囲に余分な白色が出てしまいます(図の上段)。中段や下段の画像は線画以外の部分は透明化されているので、右側の灰色背景の画面に出しても線画だけが映ります。

解決方法

物体画像の背景にあたる部分(白背景であれば白色の領域)を検出し、その部分の不透明度を 0 に、つまり透明にした画像を作成すればよい。今回の例では線画画像を扱っているので、元画像を二値化して線画の部分とそれ以外に分けると、その画像がそのままアルファチャネルの画像としても使うことができます。
ただしアルファッチャネルは 0 が透明、1 が完全な不透明に対応するので、元画像を二値化した画像そのままをアルファチャネルとして使うと、透明になって欲しい部分とそれ以外とが真逆になってしまいます(背景の白い部分が画素値が 1 なので不透明、線画部分は画素値が 0 なので透明になってしまう)。なので元画像を二値化した画像の白黒を反転させた画像をアルファチャネルとして利用します。

library( imager )
# アルファチャネル操作用の関数
applyAlpha = function( cimg, alpha, reverse = F ){
  if( class( alpha )[ 1 ] == "cimg" ){
    if( reverse ){
      if( dim( cimg )[ 4 ] >= 3 ){
        return( imappend( list( R(cimg), G(cimg), B(cimg), 1 - alpha ), axis = "c" ) )
      } else {
        return( imappend( list( cimg, 1 - alpha ), axis = "c" ) )
      }
    } else {
      if( dim( cimg )[ 4 ] >= 3 ){
        return( imappend( list( R(cimg), G(cimg), B(cimg), alpha ), axis = "c" ) )
      } else {
        return( imappend( list( cimg, alpha ), axis = "c" ) )
      }
    }
  } else { # if alpha is an array
    if( reverse ){
      a = as.cimg( 1 - alpha, x = width( cimg ), y = height( cimg ), z = 1, cc = 1 )
      if( dim( cimg )[ 4 ] >= 3 ){
        return( imappend( list( R(cimg), G(cimg), B(cimg), a ), axis = "c" ) )
      } else {
        return( imappend( list( cimg, a ), axis = "c" ) )
      }
    } else {
      a = as.cimg( alpha, x = width( cimg ), y = height( cimg ), z = 1, cc = 1 )
      if( dim( cimg )[ 4 ] >= 3 ){
        return( imappend( list( R(cimg), G(cimg), B(cimg), a ), axis = "c" ) )
      } else {
        return( imappend( list( cimg, a ), axis = "c" ) )
      }
    }
  }
}

# 画像の処理
inputImg = load.image( "image.png" )
img = as.cimg( threshold( inputImg ) )
img = applyAlpha( img, alpha = img, reverse = T )
imager::save.image( img, "saved.png" )

applyAlpha 関数は、画像に対して指定されたアルファチャネルを適用するという処理を行います。入力された cimg 画像がグレースケールであれば 2 番目のチャネルにアルファチャネルを追加し、カラーの場合は 4 番目のチャネルを書き換えます。reverse オプションは引数の alpha の値の反転を行うかどうかです。

以下はフォルダ内の画像全てについて透明化処理を行うプログラム例です。
線画画像データベースから得た20枚の画像に透明化処理を行い、また冒頭の図の下段のような線を白色にした画像も同時に作成します。
画像はこちらからダウンロードして下さい。

files = list.files( "snodgrass/subset" )
for( i in 1:20 ){
  inputImg = load.image( paste( "snodgrass/subset/", files[ i ], sep = "" ) )
  # remove background
  img = as.cimg( threshold( inputImg ) )
  img = applyAlpha( img, alpha = img, reverse = T )
  imager::save.image( img, paste( "snodgrass/nobg/", files[ i ], sep = "" ) )
  # remove background and invert black and white
  img = as.cimg( 1 - threshold( inputImg ) )
  img = applyAlpha( img, alpha = img, reverse = F )
  imager::save.image( img, paste( "snodgrass/white/", files[ i ], sep = "" ) )
}

コメント