LoginSignup
1

More than 3 years have passed since last update.

posted at

updated at

C言語でモザイク等の画像処理をしてみた

こんにちは、
今日は、C言語を用いて画像処理を行ったのですが、この実装でいいのかと疑問になったのでまとめます。
間違っているところのご指摘していただけると嬉しいです!

前提

入力画像のRGBに対応したppm形式とします。

共通処理

入力の画像に対して、幅、高さ、最大輝度などの情報を取得します。
そのあと、画像データがバイナリーとして格納されているので、取得します。
今回は、P6形式の画像を取り扱っていますので下記のようにしますが、本当はマジックナンバーによって形式が異なるので、処理が変わるので注意してください。

void read_binary_data(FILE *fp, img *img_p) {
    for (int h = 0; h < img_p -> height; h++) {
        for (int w = 0; w < img_p -> width; w++) {
            img_p -> map[h][w].r = fgetc(fp);;
            img_p -> map[h][w].g = fgetc(fp);;
            img_p -> map[h][w].b = fgetc(fp);;
        }
    }
}

画像処理

データを取得できたら、画像を処理していきます。
今回は3種類の処理をしました。

  • 左右反転
  • PGM変換
  • モザイク加工

左右反転

左右反転の処理は、出力する際に1行ずつ左右反転すればよいです。

void write_binary_data(FILE *fp, img *img) {
    for (int h = 0; h < height; h++) {
        for (int w = width; w > 0; w--) {
            fputc(img -> data[h][w].r, fp);
            fputc(img -> data[h][w].g, fp);
            fputc(img -> data[h][w].b, fp);
        }
    }
}

PGM変換

入力画像を、マジックナンバーがP4で始まるPGM画像形式に変換し、出力します。
グレースケールには、 G = 0.299*R + 0.587*G + 0.114*B という関係がありますので、これを使います。

void convert_pgm(FILE *fp, img *img_p) {
    for (int h = 0; h < img_p -> height; h++) {
        for (int w = 0; w < img_p -> width; w++) {
            fputc(img -> data[h][w].r * 0.299 + img -> data[h][w].g * 0.587 + img -> data[h][w].b * 0.114, fp);
        }
    }
}

モザイク

入力画像に対して、任意のブロック(正方形)サイズのモザイク処理をします。
まず、ブロック内のRGBの値の平均を計算します。ブロック内の全てのピクセルで計算した色にすればモザイク処理ができます。
safe_area とは、実際のブロックのサイズのことです。
例えば、モザイクのブロックのサイズが8だとします。8の倍数以外の画像サイズだと綺麗にモザイク処理ができないので、それの対策としてます。

void execute_mosaic(img *img_p, int mosaic_size) {
    int r, g, b;
    int safe_area_x, safe_area_y;

    for (int h = 0; h < img_p -> height; h += mosaic_size) {
        for (int w = 0; w < img_p -> width; w += mosaic_size) {
            r = 0;
            g = 0;
            b = 0;

            for (int y = 0; y < mosaic_size; y++) {
                if (img_p -> height <= (h + y)) break;
                safe_area_y = y + 1;

                for (int x = 0; x < mosaic_size; x++) {
                    if (img_p -> width <= (w + x)) break;
                    r += img_p -> map[h + y][w + x].r;
                    g += img_p -> map[h + y][w + x].g;
                    b += img_p -> map[h + y][w + x].b;
                    safe_area_x = x + 1;
                }
            }

            for (int y = 0; y < safe_area_y; y++) {
                for (int x = 0; x < safe_area_x; x++) {
                    img_p -> map[h + y][w + x].r = r / (safe_area_y * safe_area_x);
                    img_p -> map[h + y][w + x].g = g / (safe_area_y * safe_area_x);
                    img_p -> map[h + y][w + x].b = b / (safe_area_y * safe_area_x);
                }
            }
        }
    }
}

感想

書くほどでもなかったかなと思いますが、他によいやり方があれば教えていただきたいです。

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
What you can do with signing up
1