応用情報技術者に出てきた誤差拡散法


https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2020r02.html#02oct

画像を誤差拡散法を使い2値化するという問題をPython3で動作を確認してみました。

filerun.py.txt

問題の変数の説明を確認すると、座標が0ではなく1からスタートしています。

画像を扱った事がある人なら知ってると思いますが、一般の画像処理では座標が(0,0)からスタートしますが、この問題では(1,1)となっています。結果的にさほど影響はないかもしれないですが、勘違いの元になるのでご注意ください。

今回は問題文に合わせてX=0,Y=0の座標に空の行・列を挿入して座標を一致させる操作をする事にしました。

tkp_-CE_YL.png

下記の部分は画像を読み込んだ上で1行・1列分シフトしてbmpFromに代入していきます。

# 画像を読み込む
bmpOrg = cv2.imread('test.png')

# イメージのサイズを取得してwidth,heightに代入する
width  = bmpOrg.shape[0]
height = bmpOrg.shape[1]

# bmpOrgからグレースケールの配列
bmpFrom = np.zeros((width+1)*(height+1)).astype(np.uint8).reshape(width+1,height+1)

# bmpOrgはRGBで記録されているのでこれをグレースケールにした上でBmpFromに配置する
# この時問題文は1スタート(NOT 0スタート)なので1つずつずらす
for y in range(0,height):
    for x in range(0,width):
        bmpFrom[x+1][y+1] = int( np.average( bmpOrg[x][y] ))
次に書き出し先のbmpToを作成します。この時bmpToには負の数が入るので型がint16になる事に注意してください。

# 書き出し先用の配列(bmpTo)を作成する。bmpFromと違い、負の数があるのでint16型を使用する。
bmpTo = np.zeros((width+1)*(height+1)).astype(np.int16).reshape(width+1,height+1)
動作モードという変数がありますが、ここを0以外の数字にすると後からの画質改善の為に修正した版になります。

# 動作モード
# 0以外の数字に設定すると画質向上のための改修をしたバージョンで動作する
mode = 1
この後はほぼ問題文の通りに実装しています。

最後に、ここで使った配列はグレースケールになっています。表示する為にはRGBに戻す必要があるので処理が完了したらその処理を行います。もう負の数は出てこないので型はuint8としました。

bmpToRGB = np.zeros(width*height*3).astype(np.uint8).reshape(width,height,3)
for y in range(0,height):
    for x in range(0,width):
        tmp = bmpTo[x+1][y+1]
        bmpToRGB[x][y] = [ tmp, tmp, tmp ]
mode=0とmode=1(後半は画質改善を適用したバージョン)とではどのぐらい違うか見てみましょう。

tkp_RZivw7.png

画像の左側の赤丸で囲んだ部分は特にわかりやすいです。

tkp_c5nQDp.png

比較すると左上の何もない部分などで白黒の点の集まりがより分散して見えるので、確かに改善されていそうです。