平均画素法を2日で実装してみたよ

やあ子供たち。おじさんは風邪をひいてしまって今朝なんかもとても喉が痛いのだけど元気にしてるかい。今回は有名な画像縮小アルゴリズムの一つ、平均画素法を実装してみたのでここにメモしておくよ。

// ここに、平均画素法を実装してください。
// in (LPDWORD)bm.bmBits w, h, scale
// out (LPDWORD)lpPixel
// 平均画素法
LPDWORD src = (LPDWORD)bm.bmBits;
LPDWORD dst = lpPixel;
for (int i = 0; i < h; ++i)
{
    for (int j = 0; j < w; ++j)
    {
        DWORD& out = dst[i * w + j];
        Pix pix_out(0);

        float xs = j / scale;
        float xe = (j + 1) / scale;
        float ys = i / scale;
        float ye = (i + 1) / scale;
        int xsi = ceil(xs);
        int xei = floor(xe);
        int ysi = ceil(ys);
        int yei = floor(ye);
        float cnt = 0;
        auto pixAddr = [&src, &w0, &h0, &pix_out, &cnt]
            (int ii_s, int ii_e, int jj_s, int jj_e, float ratio)
        {
            for (int ii = ii_s; ii < ii_e; ++ii)
                for (int jj = jj_s; jj < jj_e; ++jj)
                    if ((ii > 0) && (ii < h0) && (jj > 0) && (jj < w0))
		    {
		        pix_out += Pix(src[ii*w0 + jj])*ratio;
                        cnt += ratio;
                    }
        };
        // 内部
	pixAddr(ysi, yei, xsi, xei, 1.0f);
	// 四辺
	pixAddr(ysi, yei+1, xsi - 1, xsi, xsi - xs);
	pixAddr(ysi, yei+1, xei, xei + 1, xe - xei);
	pixAddr(ysi - 1, ysi, xsi, xei+1, ysi - ys);
	pixAddr(yei, yei + 1, xsi, xei+1, ye - yei);
	// 四隅
	pixAddr(ysi - 1, ysi, xsi - 1, xsi, (xsi - xs)*(ysi - ys));
	pixAddr(ysi - 1, ysi, xei, xei + 1, (ysi - ys)*(xe - xei));
	pixAddr(yei, yei + 1, xsi - 1, xsi, (xsi - xs)*(ye - yei));
	pixAddr(yei, yei + 1, xei, xei + 1, (ye - yei)*(xe - xei));
        // 正規化
        pix_out /= cnt;
        // 
        out = pix_out.GetValue();
    }// j
}//i

ここで使用しているPixクラスはDWORD化されたRGBA値を人間が扱いやすくするための補助クラスだよ。大したものではないがとても便利だからその実装はまた別途未来日記で紹介するとして、まこんな感じのアルゴリズムだな平均画素法は。ceilとかfloorだとかをやってるところがアルゴリズムの本質部分で、まコアといえばコアかな。ratio変数とか。

ただね、ぼやけるんだよね縮小された画像が。定値関数で畳み込みしているだけの処理なのでま当然といえば当然なんだけどさ。なんとかこれをもうちょっとシャープな仕上がりにしたいところだが、そこでこの一つ前の記事の内容がお役立ちになるはずなのだけれども。まそれもまた未来日記で紹介するよ。うまくいったらな。
チャオ!