調べて実装して、評価してみた。あとアイデアをひとつだけ。
概要
- OpenCV で簡単に実装できる画像前処理を調べて実装したよ
- 実際に Object Detection する前に適用して評価したよ
- 結果は・・・微妙だったよ
モチベーション
Object Detection モデルは開発者が公開している pre-trained weight を使って遊んでいるだけの私だけど、モデルに入れる前の画像を前処理してモデルが判断しやすいようにすること、そしてモデルの出力を選別したり綺麗に整形したりするのは、使う側の責任だろうと。
ということで今回は画像前処理について実際にやってみたので、そのまとめをば。(要するに備忘録)
私は画像が専門じゃないので、OpenCV を使って簡単に実装できたものだけを。
画像は全て COCO 2017val に含まれていたものです。使っている Object Detection モデルは yolov5m で、ONNX で動かしています。もちろん使っているのはコレ。
昼白色が強い画像の場合(ホワイトバランス調整)
特に室内の画像の場合、照明の昼白色が強くて「黄色いなオイ!」と思うことが時々ある。
そういう場合はホワイトバランスの調整をして、なるべく自然色に戻してあげた方が良いんじゃないかと。
import numpy as np
import cv2
def adjust_white_balance(image: np.ndarray) -> np.ndarray:
# white balance adjustment for strong neutral white
image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
avg_a = np.average(image[:, :, 1])
avg_b = np.average(image[:, :, 2])
image[:, :, 1] = image[:, :, 1] - (
(avg_a - 128) * (image[:, :, 0] / 255.0) * 1.1
)
image[:, :, 2] = image[:, :, 2] - (
(avg_b - 128) * (image[:, :, 0] / 255.0) * 1.1
)
image = cv2.cvtColor(image, cv2.COLOR_LAB2BGR)
return image
BGR を Lab色空間に変換して、ゴニョゴニョするだけ。
ホワイトバランス調整の例1(左:調整なし、右:調整あり)
ノイズ除去(Gaussian Blur)
性能が悪いカメラだと、ノイズが乗っちゃってることがある。そういう場合はノイズ除去。
import numpy as np
import cv2
def smooth_image(image: np.ndarray) -> np.ndarray:
# image smoothing for noise removal
return cv2.GaussianBlur(image, (5, 5), 0)
めちゃ簡単。GaussianBlur で、要するにボカしてノイズを分からないようにする。
ノイズ除去の例2(左:除去なし、右:除去あり)
ノイズは、カメラの性能だけじゃなくて、例えば雨なんかもノイズの一種と見做すことが出来る。
ノイズ除去の例3(左:除去なし、右:除去あり)
暗い部分を明るくする(コントラスト調整)
暗くてよく分かんないのは、明るくしてあげたい。
import numpy as np
import cv2
def correct_contrast(image: np.ndarray) -> np.ndarray:
# contrast correction that brightens dark areas
b = 10 # HEURISTIC !!
gamma = 1 / np.sqrt(image.mean()) * b
g_table = np.array([
((i / 255.0) ** (1 / gamma)) * 255
for i in np.arange(0, 256)
]).astype("uint8")
return cv2.LUT(image, g_table)
コントラスト調整の例4(左:調整なし、右:調整あり)
暗くなると言えば逆光だが、ちょっとは効果がある。
コントラスト調整の例5(左:調整なし、右:調整あり)
やってみたけど使わないと判断したもの
ヒストグラム平坦化
import numpy as np
import cv2
def levelize_histogram(image: np.ndarray) -> np.ndarray:
# make color distributions even
for i in range(3):
image[:, :, i] = cv2.equalizeHist(image[:, :, i])
return image
暗い所を明るくするという意味で、色の分布を平坦化すればいいんじゃないの、という方法。
画像全体がすごい真っ暗!という場合は効果があるけど、普通の画像にやっちゃうと色が凄い事になっちゃうので、パス。
固定値を使ったコントラスト調整
import numpy as np
import cv2
def correct_contrast_using_lut(image: np.ndarray) -> np.ndarray:
# contrast correction using look-up-table
a = 10
c_table = np.array([
255.0 / (1 + np.exp(-a * (i - 128) / 255))
for i in np.arange(0, 256)
]).astype("uint8")
return cv2.LUT(image, c_table)
前に出したコントラスト調整は、画像のRGBの値の平均を使っているけど、これは固定値でやるパターン。これも凄いことになっちゃう。
Super-Resolution を使った鮮明化
COCOデータセットの中には、かなりボケてたりブレてたりする画像がある。こいつらを根本的にどうにかしたいなぁと考えて、思いついた。
「Super-Resolution(超解像)使えばいいんじゃね?」
dnn_superres を使うので、Python Library として普通の opencv-python
じゃなくて opencv-contrib-python
をインストールすることが必要です。
import numpy as np
import cv2
from cv2 import dnn_superres, dnn_superres_DnnSuperResImpl
sr = dnn_superres.DnnSuperResImpl_create()
sr.readModel('superres/ESPCN_x4.pb')
sr.setModel('espcn', 4)
def upsample_image(
image: np.ndarray,
sr: dnn_superres_DnnSuperResImpl
) -> np.ndarray:
# increase resolution with super-resolution to make the image clearer
# and then shrink the image
prev_height = image.shape[0]
prev_width = image.shape[1]
# sr.upsample() (super-resolution) is too slow when the image is big
# so, do super-resolution after shrinking image
image = cv2.resize(
image, (prev_width // 2, prev_height // 2),
interpolation=cv2.INTER_AREA
)
image = sr.upsample(image)
# restore image size
image = cv2.resize(
image, (prev_width, prev_height),
interpolation=cv2.INTER_AREA
)
return image
適当なサンプルプログラムで済まない。
- SuperResolutionのモデルとしては、一番高速なESPCNを使う
- ESPCNと言えども、画像のサイズが大きいと時間がかかるので、まずは半分に縮小する
- ESPCN で4倍に拡大する
- それをまた半分に縮小して、元のサイズに戻す
意図としては
- 画像を縮小することでそもそもノイズを減らす効果を期待
- 拡大に単なる補間ではない SuperResolution を使うことで、さらに滑らかになることを期待
SuperResolutionをやった例6(左:やってない、右:やった)
性能
coco dataset 2017val からランダムに1000枚を選んで計測した。
-
no_image_clarify
: 画像前処理なし -
white_balance
: ホワイトバランス調整のみ -
smoothing
: ノイズ除去のみ -
contrast
: コントラスト調整のみ -
superres
: SuperResolutionのみ -
all
: 上記全部入り(画像に適用した順番は上の通り) -
all2
: コントラスト調整すると、ノイズが乗ったみたいになるので、コントラスト調整したあとにノイズ除去した -
all2_no_superres
:all2
から SuperResolution だけやめる
所要時間
画像1枚あたりの平均
なんで all
が前処理単体よりも低いんや・・・まぁ、Object Detection モデルの inference に要する時間に比べると、どれも誤差の範囲内ってことなんだろう。
精度
まとめ
- 前処理を入れれば入れるほど精度が落ちてるのは、結局、特殊な画像だと良くなったように見えるけど、全体に対して一律に適用しちゃうと悪影響を及ぼすということなんだろうな
- 作っては見たものの、デフォルトで OFF にするよう修正します・・・
- ちゃんと評価して良かった(今日の良かった探し)
-
http://farm1.staticflickr.com/147/434840964_a9c5bc3b58_z.jpg
confidence score 的には大して変わってないけど、真ん中の人やソファーの上の laptop のスコアがちょっと上がってるか。 ↩ -
http://farm4.staticflickr.com/3265/5839036384_1ecac4f59a_z.jpg
よく見ると分かるけど、元のザラザラしたノイズがすっきりしている。
Object Detection 的にも、上部の大きな bowl がギリギリ認識できるようになった。 ↩ -
http://farm1.staticflickr.com/5/7213514_c15cc3c285_z.jpg
この場合、車も消火栓も confidence score が上がっている。 ↩ -
http://farm2.staticflickr.com/1100/538718655_aef41a4977_z.jpg
traffic light が1個消えちゃったけど、車の confidence score は上昇している。 ↩ -
http://farm6.staticflickr.com/5184/5694114164_e2a5d24659_z.jpg
右側では、海に入っているサーファーを頑張って認識しようとしているのが分かる。 ↩ -
http://farm3.staticflickr.com/2258/2269567997_08065fe404_z.jpg
こんなブレブレのやつでも、ちょっとはマシになるかな、と。 ↩