【画像処理】Pythonを使ってネガポジ反転を自動化してみた！

#概要
実験などで，オシロスコープの波形を保存し，レポートに貼り付けるなんてことがあったのですが，
オシロスコープの波形は黒ベースの画像なので，そのまま貼り付けると，見づらいかつ，大量のインクを使ってしまうため，
あるアプリケーションで色を反転してから，レポートに貼っていました．

しかし，1枚1枚色を反転してというのは非常にめんどくさい！

ということで，**画像のネガポジ反転**をPythonを使って**自動化**してみました！

#動作環境
- Windows10(64bit)
- Python 3.7.2
- OpenCV 4.1.0 

#やりたいこと
上の画像を下の画像のようにするのが目標です．

![sin.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433790/3ab5ebdb-5b62-b348-2a24-f54acde3e768.jpeg)

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

![sin.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433790/460c7c2a-d952-b824-5eb9-046ad910187b.jpeg)

#どのようにしてネガポジ反転を行うのか
OpenCVにあるbitwise_not()関数を使ってあげれば，ネガポジ反転した画像を得られます．非常に簡単ですね．
bitwiseとはビット単位という意味です．それをnotすることで反転するというイメージでしょうか？
もしくは，関数を使わなくても，以下の1行でもできます．numpy恐ろしやですね．
計算時間も%timeitコマンドで調べてみましたが，数枚程度の画像であればそれほど変わりません．

```py
img2 = 255 - img
```



#ネガポジ反転させる関数の定義
では，ネガポジ反転させる関数を定義します．これがこのプログラムの核となる部分です．

```py
# 画像のネガポジ反転をする関数
def negaposi(image):
    # 画像の読み込み
    img = cv2.imread(image)

    # ネガポジ反転
    img2 = cv2.bitwise_not(img)

    return img, img2
```

#コードの解説
では解説をしていきます．

- 画像の読み込み

まずは，画像を読み込ませます．

```py
    img = cv2.imread(image)
```

- ネガポジ反転

次に，先ほど説明したbitwise_not()関数を用いて，ネガポジ反転をさせます．

```py
    img2 = cv2.bitwise_not(img)
```

あとはこの関数の返り値に対して，画像を表示したり保存したりすれば，所望の画像を保存することができます．

#複数の画像に対して一気にネガポジ反転を行う
では複数の画像に対して，一気にネガポジ反転をやってみようと思います．
ネガポジ反転させたい画像がimages_for_negaposiディレクトリにあり，拡張子は.bmpであるとします．
そして保存先のディレクトリはnegaposi_imagesとします．

```py
INPUTDIR = 'images_for_negaposi'
OUTPUTDIR = 'negaposi_images'
EXT = 'bmp'

# 編集後の画像の保存ディレクトリの作成
if not os.path.isdir(OUTPUTDIR):
    os.mkdir(OUTPUTDIR)

# ORIGINALFILE内の全ての画像に対してループ
for image in glob.glob(INPUTDIR + '/*.' + EXT):
    img, img2 = negaposi(image)
    # cv2.imshow('unedited', img) # 編集前の画像

    # 相対パスの部分を削除
    image = image[len(INPUTDIR) + 1:] 

    # ネガポジ反転した画像を表示
    cv2.imshow(image, img2)
    # cv2.moveWindow('unedited', 400, 0)
    # cv2.moveWindow(image, 0, 0) # 画像表示の位置変更
    cv2.waitKey(0) # 何らかのキーが押されるまで待機
    cv2.destroyAllWindows() # 全てのウィンドウを削除

    # 画像を保存
    cv2.imwrite(OUTPUTDIR + '/' + image, img2) # 画像の保存
```

#コードの解説
それでは，コードの解説をしていきます．

- 保存用ディレクトリの作成

保存先のディレクトリを作成します．同名のファイルがあるとエラーが出てしまうので，isdir()関数で，引数名のディレクトリがあるか否かを確認し，なければmkdir()関数で作成します．

```py
if not os.path.isdir(OUTPUTDIR):
    os.mkdir(OUTPUTDIR)
```

- ループ

INPUTDIR内の全ての画像に対して，ループします．
glob.glob()は引数パターンにマッチするファイルリストの作成を行います．
ここで，*はいわゆるワイルドカードです．
EXTは拡張子extensionの略です。

```py
for image in glob.glob(INPUTDIR + '/*.' + EXT):
```

- 相対パスの部分を削除

glob.glob()関数は，パターンにマッチしたファイルを相対パスの形で返すので，相対パスの部分を削除することで，
元のファイルと同じ名前で，ネガポジ反転後のファイルを保存できます．
具体的に説明すると，今の場合には，imageはimages_for_negaposi/*.bmpなので，最初のimages_for_negaposi/の部分をスライスによって削除します．
[start:]はstratオフセットから末尾までのシーケンスを抽出します．ちなみにオフセットはゼロから始まります．

```py
    image = image[len(INPUTDIR) + 1:] 
```

- 画像の表示

1行目で画像を表示します．これだけだと，すぐにウィンドウが消えてしまうので，
waitKey(0)で何らかのキーが入力されるまで，ここで待機します．
何らかのキーが押されたら，プログラムを再開し，destroyAllWindows()で全てのウインドウを削除します．
コメントアウトの部分は，ネガポジ反転前の画像も表示したいときに使います．
そのままimshow()だけだと，ウインドウが重なってしまうことがあるため，moveWindow()でウインドウを移動させます．

```py
    cv2.imshow(image, img2)
    # cv2.moveWindow('unedited', 400, 0)
    # cv2.moveWindow(image, 0, 0) # 画像表示の位置変更
    cv2.waitKey(0) # 何らかのキーが押されるまで待機
    cv2.destroyAllWindows() # 全てのウィンドウを削除
```

- 画像の保存

最後に画像を保存します．ここでも保存先はもちろん相対パスで指定します．

```py
    cv2.imwrite(OUTPUTDIR + '/' + image, img2) # 画像の保存
```


#まとめ
いかがだったでしょうか？今回はOpenCVを使って画像をネガポジ反転させる方法を説明しました．
これを使えばとても効率的ですね．ぜひ，活用してみてください．
また，プログラムに関する建設的なフィードバックもお待ちしています！