画像を鮮明にするには色や形の面で鮮明にする必要があります。
という訳で実際にプログラムで画像を加工します。
使用する画像
画像の読み込み
img = cv.imread("lena_square.png", cv.IMREAD_GRAYSCALE)
plt.imshow(img, cmap="gray")
画像を行列に変換
ary = np.array(img)
ary
出力結果はこうなります
array([[169, 169, 168, ..., 175, 162, 138],
[169, 169, 168, ..., 175, 162, 138],
[169, 169, 168, ..., 175, 162, 138],
...,
[ 53, 53, 59, ..., 115, 112, 114],
[ 53, 53, 64, ..., 117, 118, 122],
[ 53, 53, 64, ..., 117, 118, 122]], dtype=uint8)
色の強調(コントラスト強調)
コントラスト強調をするにあたってまず画像の画素値のヒストグラムを見てみましょう。
hist = []
for i in range(256):
hist.append(0)
for i in range(len(ary)):
for j in range(len(ary[i])):
hist[ary[i][j]] = hist[ary[i][j]] + 1
x = []
for i in range(256):
x.append(i)
plt.bar(x, hist)
plt.show()
ここで画像の色が鮮明でないのは似た値の色が集まっているからです。ここだと50から225までに集まっていることが分かると思います。
アプローチとしては、累積ヒストグラムを作成して累積ヒストグラムを0から255の範囲の値にし、元の画像の画素値に対応する累積ヒストグラムの値を使います。
hist = np.array(hist)
histsum = np.cumsum(hist)
for i in range(len(histsum)):
histsum[i] = int((histsum[i] / max(histsum)) * 255)
plt.bar(x, histsum)
plt.show()
ここから実際に当てはめていきます。
ctt = []
for i in range(len(ary)):
tmp = []
for j in range(len(ary[i])):
tmp.append(histsum[ary[i][j]])
ctt.append(tmp)
plt.imshow(ary, cmap="gray")
plt.show()
plt.imshow(ctt, cmap="gray")
plt.show()
強調後のヒストグラム
for i in range(len(ctt)):
for j in range(len(ctt[i])):
hist[ctt[i][j]] = hist[ctt[i][j]] + 1
x = []
for i in range(256):
x.append(i)
plt.bar(x, hist)
plt.show()
この結果から最初のヒストグラムが横に伸びたことが分かると思います。このようにバランスよく画素値を使うことで色を鮮明にします。
輪郭抽出
縦に輪郭を抽出と横に輪郭を抽出と縦横で輪郭を抽出する方法をやります。
やり方としては行列において縦に輪郭を抽出する場合はある行と次の行の差分を取り、横に輪郭を抽出する場合はある列と次の列の差分を取り、両方の場合は両方やります。
edgh = []
edgw = []
edge = []
# 縦に輪郭抽出
for i in range(len(ary)-1):
tmp = []
for j in range(len(ary)):
tmp.append(abs(ary[i+1][j]-ary[i][j]))
edgh.append(tmp)
# 横に輪郭抽出
for i in range(len(ary)):
tmp = []
for j in range(len(ary)-1):
tmp.append(abs(ary[i][j+1]-ary[i][j]))
edgw.append(tmp)
# 縦横輪郭抽出
for i in range(len(ary)-1):
tmp = []
for j in range(len(ary)-1):
tmp.append(abs(int(ary[i][j+1]-ary[i][j]+ary[i+1][j]-ary[i][j])/2))
edge.append(tmp)
plt.imshow(ary, cmap="gray")
plt.show()
plt.imshow(edgh, cmap="gray")
plt.title("edge height")
plt.show()
plt.imshow(edgw, cmap="gray")
plt.title("edge width")
plt.show()
plt.imshow(edge, cmap="gray")
plt.title("edge all")
plt.show()
輪郭強調
輪郭強調はこれは一般的なやり方ではありませんが、縦横の差分の平均値を取り(ただし輪郭抽出と異なり絶対値は使わない)白黒を反転させて元の画像と抽出した輪郭の画像の画素値の平均値の画像を作り、作られた画像に更に元の画像の画素値の平均値を取ります。
するとこんな感じの事ができます。
mask = []
for i in range(len(ary)):
tmp = []
for j in range(len(ary)):
if i == len(ary) - 1:
tmp.append(0)
elif j == len(ary[i]) -1:
tmp.append(0)
else:
tmp.append(ary[i][j]-(ary[i+1][j]-ary[i][j+1])/2)
mask.append(tmp)
mask = np.array(mask)
mask = 255 - mask
dst = (ary + mask) / 2
dst = (dst + ary) /2
for i in range(len(dst)):
for j in range(len(dst[i])):
if dst[i][j] > 255:
dst[i][j] = 255
elif dst[i][j] < 0:
dst[i][j] = 0
plt.imshow(ary, cmap="gray")
plt.show()
plt.imshow(dst, cmap="gray")
plt.show()
まとめ
今回は画像を鮮明にする技術としてコントラスト強調、輪郭抽出、輪郭強調を扱いました。現代では生成AIで更に凄い鮮明化だけでなく拡大しても自然な画像を作る事もできますが、昔はこういう風にやっていたという歴史があったということです。
あとこれらのプログラミングは微分や統計学や線形代数の概念が必要なので数学を疎かにしてはいけません(自分への戒め)。