すべての画像はこの猫(neko.jpg)の写真で変更したものです。

neko.jpg

画素ごとの濃淡変換

画素ごとの濃淡変換:g(i,g)=α×f(i,j)g(i, g)=\alpha\times 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)

ポスタリゼーション

二値化

  • 二段階に制限したポスタリゼーション
    • 半分より暗い → 0
    • 半分より明るい → 255

(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] # filter的size
boundary_len = int(filter_shape / 2) # 图像外围加几行几列
arr_shape = arr.shape # 图像的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=wwm=wwf(i+m,j+m)h(m,n)g(i, j)=\sum_{n=-w}^{w}\sum_{m=-w}^{w}f(i+m, j+m)h(m, n)
    • 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],
])

平均フィルタ

加重平均フィルタ

  • フィルタの原点に近いほど大きな重みを与える
  • 純粋な平均家よりも自然になる
  • 重みをガウス分布に近づけたものをガウシアンフィルタという

以下のコードで用いたフィルタはガウシアンフィルタ(もちろんそれも加重平均フィルタの一種であり)です、ご注意ください!

3x3と5x5の重み付け(ガウシアンフィルタ)

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 # filter的size
boundary_len = int(filter_shape / 2) # 图像外围加几行几列
arr_shape = arr.shape # 图像的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)

メディアンフィルタ: size(5)

ですが、このメディアンフィルタはある範囲の中央値を取るので、効果は平均化フィルタと似ている、画像もぼかされます。ですが、平滑化フィルタ(平均化フィルタ、加重平均フィルタ)と比べると、このメディアンフィルタはコントラストの差がある輪郭の部分がぼやけにくいというメリットがあります、ですので、画像解析を実行する前に、ノイズを除去(じょきょ)するのにいいと。

鮮鋭化(せんえいか)

このフィルタでは、平均化フィルタとは逆に、画像のエッジを強調(先鋭化)することでシャープな画像が得られます。ただし、画像にノイズが含まれている場合、ノイズまで強調されてしまう場合があります。

エッジ抽出

  • 微分フィルタ
    • 連続関数の微分の定義:f(x)=limn0f(x+h)f(x)hf^{'}(x)=\displaystyle\lim_{n\to0}\frac{f(x+h)-f(x)}{h}
    • 画像処理の場合: 隣接画素との差分の平均値

一次微分フィルタ

  • 画像の濃淡が急激に変化するエッジ部分を抽出できるが、ノイズを強調する(欠点)
  • 微分した後に直行する方向に平均化を行う(ノイズを消すため)

一次微分フィルタ(3x3)

縦方向微分

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],
])

横2次微分フィルタ

縦2次微分フィルタ

ラプラシアン

2x2f(x,y)+2y2f(x,y)\frac{\partial^2}{\partial x^2 }f(x, y) + \frac{\partial^2}{\partial y^2}f(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