すべての画像はこの猫(neko.jpg)の写真で変更したものです。
画素ごとの濃淡変換
画素ごとの濃淡変換:g(i,g)=α×f(i,j)
ネガポジ変転
例:
(100, 150, 0)→(155, 105, 255)
1 2 3 4 5 6 7 8
| from skimage import io
source_path = 'neko.jpg' target_path = 'tar_neko.jpg' img = io.imread(source_path)
negapoji = 255 - img img = io.imsave(target_path, negapoji)
|
ポスタリゼーション
ガウス関数を使います
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from skimage import io source_path = 'neko.jpg' target_path = 'tar_neko.jpg' img = io.imread(source_path) level = 6 step = 255 / level steps = [i * step for i in range(level)] def process(value: int) -> int: for i in steps: if value < i: return i return value for row in range(img.shape[0]): for col in range(img.shape[1]): img[row, col][0] = process(img[row, col][0]) img[row, col][1] = process(img[row, col][1]) img[row, col][2] = process(img[row, col][2]) io.imsave(target_path, img)
|
二値化
(RGB三个值需要分别进行计算)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from skimage import io source_path = 'neko.jpg' target_path = 'tar_neko.jpg' img = io.imread(source_path) def process(value: int) -> int: for i in steps: if value < 128: return 0 else: return 255 for row in range(img.shape[0]): for col in range(img.shape[1]): img[row, col][0] = process(img[row, col][0]) img[row, col][1] = process(img[row, col][1]) img[row, col][2] = process(img[row, col][2]) io.imsave(target_path, img)
|
フィルタリング
- 平滑化
- エッジ抽出
- 2次微分フィルタ
- ラプラシアンフィルタ
コードに関する
フィルタリングの部分に、計算するとき、全てが元の画像の周りに0で補足し、以下のような自作アルゴリズムで実行します。
その中で、xxx_filter
は各フィルタです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| from skimage import io import numpy as np source_path = 'neko.jpg' target_path = 'tar_neko.jpg' img = io.imread(source_path) i = 5 xxx_filter = np.array([[x]*3]*3) def range_limit(number: int) -> int: if number < 0: return -number elif number > 255: return 255 else: return number def process(arr: np.array, filter: np.array): filter_shape = filter.shape[0] boundary_len = int(filter_shape / 2) arr_shape = arr.shape new_img = np.zeros((arr_shape[0] + boundary_len * 2, arr_shape[1] + boundary_len * 2, arr_shape[2]), dtype=np.uint8) new_img2 = arr.copy() for row in range(arr_shape[0]): for col in range(arr_shape[1]): new_img[row + boundary_len, col + boundary_len] = arr[row, col] for row in range(arr_shape[0]): for col in range(arr_shape[1]): r, g, b = 0, 0, 0 for i in range(filter_shape): for j in range(filter_shape): r += new_img[row + i, col + j, 0] * filter[i, j] g += new_img[row + i, col + j, 1] * filter[i, j] b += new_img[row + i, col + j, 2] * filter[i, j] r = range_limit(r) g = range_limit(g) b = range_limit(b) new_img2[row, col] = np.array([r, g, b], dtype=np.uint8) return new_img2 img_new = process(img, xxx_filter) io.imsave(target_path, img_new)
|
ただし、このコードは単純に試しに作ったものです、効率的には良くないもので
もし使おうとする場合、以下のようにcv2ライブラリ使っていただければ、効率的にも、美学的でも良い。
1 2 3 4 5 6 7
| import cv2 source_path = 'neko.jpg' target_path = 'tar_neko.jpg' img = cv2.imread(source_path) xxx_filter = np.array([[x]*3]*3) new_img = cv2.filter2D(img, -1, xxx_filter, borderType=cv2.BORDER_CONSTANT).astype("uint8") cv2.imwrite(target_path, new_img)
|
これからの各部分にフィルタのみを書きます。
フィルタリングとは
- 線形フィルタ
- g(i,j)=∑n=−ww∑m=−wwf(i+m,j+m)h(m,n)
- h(m,n)=フィルタの係数
- 非線形フィルタ
平滑化(ぼかし)
平滑化とは:なめらかな濃淡化を画像に与えること(ぼかし効果)
フィルタの範囲内の画素値の平均値を出力画像の画素値とする。この効果によりボケた画像の濃淡変化はな滑らかで画像に含まれるノイズなどが減少されてます。
また平均化フィルタには、単純な平均値ではなく、注目画素に近いほど大きな重みをつける「加重平均化フィルタ」があります。重みの分布を正分布の近似値(きんじち)とする場合、特に「ガウシアンフィルタ」と呼びます。一般的に、加重平均フィルタのほうがより自然な平滑化を行うことができます。
平均化フィルタ
1 2
| i = 3 average_filter = np.array([[1/(i*i)]*i]*i)
|
i=3の場合、このフィルタは以下のようになります
1 2 3 4 5
| average_filter = np.array([ [1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9], ])
|
加重平均フィルタ
- フィルタの原点に近いほど大きな重みを与える
- 純粋な平均家よりも自然になる
- 重みをガウス分布に近づけたものを
ガウシアンフィルタ
という
以下のコードで用いたフィルタはガウシアンフィルタ(もちろんそれも加重平均フィルタの一種であり)です、ご注意ください!
1 2 3 4 5
| weight_average_filter = np.array([ [1/16, 2/16, 1/16], [2/16, 4/16, 2/16], [1/16, 2/16, 1/16], ])
|
違い
加重平均はぼかしが弱い、つまりもっと自然に近づく
メディアンフィルタ
普段は平均化フィルタを用いて、画像をぼかしてノイズを減少させるですが、実際に画像認識するとき、輪郭の部分が重要で、ぼかした画像には輪郭もぼかされました。
「輪郭をなるべくぼやかさずに、ノイズを除去したい」のため、メディアンフィルタは、平均値ではなく、その中央値を選ぶのです。
上の画像のように、カーネルに被される部分の画素値を順番に並んで、真ん中にある112を選べ、出力画像の対応する座標の画素値を112に。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from skimage import io import numpy as np source_path = 'neko.jpg' target_path = 'tar_neko.jpg' img = io.imread(source_path) def process(arr: np.array, size: int = 3): filter_shape = size boundary_len = int(filter_shape / 2) arr_shape = arr.shape new_img = np.zeros((arr_shape[0] + boundary_len * 2, arr_shape[1] + boundary_len * 2, arr_shape[2]), dtype=np.uint8) new_img2 = arr.copy() for row in range(arr_shape[0]): for col in range(arr_shape[1]): new_img[row + boundary_len, col + boundary_len] = arr[row, col] for row in range(arr_shape[0]): for col in range(arr_shape[1]): r, g, b = [], [], [] for i in range(filter_shape): for j in range(filter_shape): r.append(new_img[row + i, col + j, 0]) g.append(new_img[row + i, col + j, 1]) b.append(new_img[row + i, col + j, 2]) new_img2[row, col] = np.array([np.median(r), np.median(g), np.median(b)], dtype=np.uint8) return new_img2 img_new = process(img, 5) io.imsave(target_path, img_new)
|
ですが、このメディアンフィルタはある範囲の中央値を取るので、効果は平均化フィルタと似ている、画像もぼかされます。ですが、平滑化フィルタ(平均化フィルタ、加重平均フィルタ)と比べると、このメディアンフィルタはコントラストの差がある輪郭の部分がぼやけにくいというメリットがあります、ですので、画像解析を実行する前に、ノイズを除去(じょきょ)するのにいいと。
鮮鋭化(せんえいか)
このフィルタでは、平均化フィルタとは逆に、画像のエッジを強調(先鋭化)することでシャープな画像が得られます。ただし、画像にノイズが含まれている場合、ノイズまで強調されてしまう場合があります。
エッジ抽出
- 微分フィルタ
- 連続関数の微分の定義:f′(x)=n→0limhf(x+h)−f(x)
- 画像処理の場合: 隣接画素との差分の平均値
一次微分フィルタ
- 画像の濃淡が急激に変化するエッジ部分を抽出できるが、ノイズを強調する(欠点)
- 微分した後に直行する方向に平均化を行う(ノイズを消すため)
縦方向微分
1 2 3 4 5
| tate_bibun_filter = np.array([ [0, -1, 0], [0, 0, 0], [0, 1, 0], ])
|
横方向微分
1 2 3 4 5
| yoko_bibun_filter = np.array([ [0, 0, 0], [-1, 0, 1], [0, 0, 0], ])
|
プリューウィットフィルタ
2つのフィルタを一つに
元のフィルタの一方を入力画像とみなし、もう一方を上下左右を変転させて施す
方法
入力画像を横方向微分フィルタで処理して、縦方向で(平均化フィルタを使って)平滑化する。
入力画像を縦方向微分フィルタで処理して、横方向で(平均化フィルタを使って)平滑化する。
1 2 3 4 5 6 7 8 9 10
| yoko_prewitt_filter = np.array([ [-1, 0, 1], [-1, 0, 1], [-1, 0, 1], ]) tate_prewitt_filter = np.array([ [-1, -1, -1], [0, 0, 0], [1, 1, 1], ])
|
ソーベルフィルタ
入力画像を横方向微分フィルタで処理して、縦方向で(加重平均化フィルタを使って)平滑化する。
入力画像を縦方向微分フィルタで処理して、横方向で(加重平均化フィルタを使って)平滑化する。
1 2 3 4 5 6 7 8 9 10
| yoko_sober_filter = np.array([ [-1, 0, 1], [-2, 0, 2], [-1, 0, 1], ]) tate_sober_filter = np.array([ [-1, -2, -1], [0, 0, 0], [1, 2, 1], ])
|
2次微分とラプラシアン
2次微分フィルタ
1 2 3 4 5 6 7 8 9 10
| yoko_nijibibin_filter = np.array([ [0, 0, 0], [1, -2, 1], [0, 0, 0], ]) tate_nijibibin_filter = np.array([ [0, 1, 0], [0, -2, 0], [0, 1, 0], ])
|
ラプラシアン
∂x2∂2f(x,y)+∂y2∂2f(x,y)
横方向、縦方向2次微分フィルタの和
1 2 3 4 5
| laplace_filter = np.array([ [0, 1, 0], [1, -4, 1], [0, 1, 0], ])
|
参考になった内容
画像フィルタ~より容易な欠陥検出のために(前編): https://www.visco-tech.com/newspaper/column/detail19/
ガウシンアンフィルタ - ノイズの除去: https://www.mitani-visual.jp/mivlog/imageprocessing/gf3r89.php
画像処理の鮮鋭化フィルタ適用後の0~255から外れた画素値の補正方法を知りたい: https://teratail.com/questions/232764
图像锐化和边缘检测算子分析与实现: https://blog.csdn.net/qq26983255/article/details/101467503
メディアンフィルタ - ノイズの除去: https://www.mitani-visual.jp/mivlog/imageprocessing/medf368.php
1次微分フィルタ Prewitt(プレヴィット)フィルタ - 画像のエッジ抽出: https://www.mitani-visual.jp/mivlog/imageprocessing/edgefilter001.php
几种图像锐化算子的比较: https://zhuanlan.zhihu.com/p/346892999
【图像处理】轻松搞懂图像锐化: https://zhuanlan.zhihu.com/p/162275458