3
2

画像を鮮明にする技術

Last updated at Posted at 2024-04-28

画像を鮮明にするには色や形の面で鮮明にする必要があります。
という訳で実際にプログラムで画像を加工します。
使用する画像
Untitled.png

画像の読み込み

img = cv.imread("lena_square.png", cv.IMREAD_GRAYSCALE)
plt.imshow(img, cmap="gray")

Untitled.png

画像を行列に変換

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()

Untitled.png
ここで画像の色が鮮明でないのは似た値の色が集まっているからです。ここだと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()

Untitled.png

ここから実際に当てはめていきます。

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()

Untitled.png
Untitled-1.png

強調後のヒストグラム

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()

Untitled.png
この結果から最初のヒストグラムが横に伸びたことが分かると思います。このようにバランスよく画素値を使うことで色を鮮明にします。

輪郭抽出

縦に輪郭を抽出と横に輪郭を抽出と縦横で輪郭を抽出する方法をやります。
やり方としては行列において縦に輪郭を抽出する場合はある行と次の行の差分を取り、横に輪郭を抽出する場合はある列と次の列の差分を取り、両方の場合は両方やります。

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()

Untitled.png
Untitled-1.png
Untitled.png
Untitled-1.png

輪郭強調

輪郭強調はこれは一般的なやり方ではありませんが、縦横の差分の平均値を取り(ただし輪郭抽出と異なり絶対値は使わない)白黒を反転させて元の画像と抽出した輪郭の画像の画素値の平均値の画像を作り、作られた画像に更に元の画像の画素値の平均値を取ります。
するとこんな感じの事ができます。
image.png

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()

Untitled.png
Untitled-1.png
なんというか、彫刻みたいな感じになりましたね。

まとめ

今回は画像を鮮明にする技術としてコントラスト強調、輪郭抽出、輪郭強調を扱いました。現代では生成AIで更に凄い鮮明化だけでなく拡大しても自然な画像を作る事もできますが、昔はこういう風にやっていたという歴史があったということです。
あとこれらのプログラミングは微分や統計学や線形代数の概念が必要なので数学を疎かにしてはいけません(自分への戒め)。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2