本投稿では、Pythonを用いて画像にノイズを加えていきます。
加えるノイズ
今回実装するノイズは以下の3つです。

インパルスノイズとは?
インパルスのいずは、画像処理分野で広く研究されている画像劣化のモデルであり、以下の式で定義されています。
$$ x’_{ij} = \begin{equation} \left\{ \, \begin{aligned} & n_{ij} & 確率p \\ & x_{ij} & 確率(1-p) \end{aligned} \right. \end{equation} $$
塩ごまノイズとは?
塩ごまノイズは、インパルスノイズの確率pの値が0.5の時のノイズである
$$ x’_{ij} = \begin{equation} \left\{ \, \begin{aligned} & n_{ij} & 確率0.5 \\ & x_{ij} & 確率0.5 \end{aligned} \right. \end{equation} $$
ガウシアンノイズとは?
ガウス雑音(ガウスざつおん)は正規分布(ガウス分布ともいう)と等しい確率密度関数を持つ統計的雑音[1][2]。言い換えると、ノイズがとる値がガウス分布であるということである。
wiki
ガウス確率変数をzとする確率密度関数pは以下のようになる。
$$ P_G(Z) = \frac{1}{σ\sqrt{2\pi} \\}exp(-\frac{(z-\mu)^2}{2\sigma^2}) $$
Pythonで実装
全コード
# ライブラリ読み込み
import cv2
import numpy as np
import random
class NoiseImage:
def __init__(self):
self.img = None
# 画像読み込み
def image_read(self, filename, gray=False):
if gray == True:
img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
else:
img = cv2.imread(filename)
self.img = img
return img
# ノイズ付与関数
def noise_in_image(self, noise_name, noise_level=0.02, rate=None, ave=0, sigma=0.01):
if noise_name == "impulse":
noise_img = impulse(self.img, noise_level, rate)
elif noise_name == "salt&pepper":
noise_img = salt_and_pepper(self.img, noise_level)
elif noise_name == "gaussian":
noise_img = gaussian(self.img, ave, sigma)
return noise_img
# インパルスノイズ
def impulse(img, noise_level, rate=0.5):
img_size = (img.shape[0]) * (img.shape[1])
noise_sum = int(img_size * noise_level)
noise_xy_random = random.sample(range(1, img_size+1), k=noise_sum)
if len(img.shape) == 2: # グレースケール用
noise_img = img
for i in range(len(noise_xy_random)):
noise_fig = np.random.choice([0, 255], p=[rate, 1-rate])
x = int((noise_xy_random[i] - 1) / img.shape[0])
y = (noise_xy_random[i] - 1) - (img.shape[1] * x)
noise_img[x][y] = noise_fig
elif len(img.shape) == 3: # カラー用
channels = 3
noise_img = img
for i in range(len(noise_xy_random)):
noise_fig = np.random.choice([0, 255], p=[rate, 1-rate])
x = int((noise_xy_random[i] - 1) / img.shape[0])
y = (noise_xy_random[i] - 1) - (img.shape[1] * x)
for j in range(channels):
noise_img[x][y][j] = noise_fig
return noise_img
# 塩ごまノイズ
def salt_and_pepper(img, noise_level):
rate = 0.5
img_size = (img.shape[0]) * (img.shape[1])
noise_sum = int(img_size * noise_level)
noise_xy_random = random.sample(range(1, img_size+1), k=noise_sum)
if len(img.shape) == 2: # グレースケール用
for i in range(len(noise_xy_random)):
noise_img = img
noise_fig = np.random.choice([0, 255], p=[rate, 1-rate])
x = int((noise_xy_random[i] - 1) / img.shape[0])
y = (noise_xy_random[i] - 1) - (img.shape[1] * x)
noise_img[x][y] = noise_fig
elif len(img.shape) == 3: # カラー用
channels = 3
noise_img = img
for i in range(len(noise_xy_random)):
noise_fig = np.random.choice([0, 255], p=[rate, 1-rate])
x = int((noise_xy_random[i] - 1) / img.shape[0])
y = (noise_xy_random[i] - 1) - (img.shape[1] * x)
for j in range(channels):
noise_img[x][y][j] = noise_fig
return noise_img
# ガウシアンノイズ
def gaussian(img, ave=0, sigma=0.01):
noise = np.random.normal(0, sigma, np.shape(img))
img = img + noise
img[img > 255] = 255
img[img < 0] = 0
noise_img = img.astype(np.uint8)
return noise_img
if __name__ == "__main__":
# 画像読み込み
filename = "Lenna.bmp"
noise_class = NoiseImage()
img = noise_class.image_read(filename, gray=True)
# ノイズ付与
## インパルスノイズ
# noise_img = noise_class.noise_in_image("impulse", noise_level=0.06, rate=0.8)
# cv2.imwrite("LennaImpulseNoise.bmp", noise_img)
## 塩ごまノイズ
noise_img = noise_class.noise_in_image("salt&pepper")
cv2.imwrite("LennaSaltPepperNoise.bmp", noise_img)
## ガウシアンノイズ
# noise_img = noise_class.noise_in_image("gaussian", ave=0, sigma=30)
# cv2.imwrite("LennaGausNoise.bmp", noise_img)
出力結果
次の画像は、ノイズを加える前のオリジナル画像です。

次にノイズを付与した画像です。
左から、インパルスノイズ、塩ごまノイズ、ガウシアンノイズです。



参考文献
- セルオートマトンによる多値画像のノイズ低減手法(評価方法の検討)情報処理学会研究報告 IPSJ SIG Technical Report <= pdfがダウンロードされます。
コメント