【C言語/画像処理】PPM・PGM・PBMの相互変換プログラム【ライブラリ不要】

PNMフォーマット相互変換のプログラム紹介ページアイキャッチ

下記ページで PNM(PPM・PGM・PBM)ファイルから画像データを読み込んだり、画像データを PNM ファイルに書き込みを行う関数の紹介を行いました。

pnmの読み込み書き出し方法解説ページのアイキャッチ【C言語/画像処理】PNM(PPM・PGM・PBM)ファイルの読み込みと書き出し【ライブラリ不要】

このページでは上記ページで紹介した関数を利用して、PPM・PGM・PBM の各フォーマットを相互に変換するプログラムの紹介をしていきたいと思います。

上記ページで紹介した関数の使い方の参考になると思いますし、各フォーマットをどのように変換すれば良いかの参考にもなると思います!

PNM の各フォーマットを相互変換するプログラム

下記が PNM の各フォーマットを相互変換するプログラムになります。

ソースコード

画像フォーマットを変換するプログラム
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define MAX_VALUE 255

/* PNMのタイプ */
typedef enum {
    PNM_TYPE_PBM_ASCII,
    PNM_TYPE_PGM_ASCII,
    PNM_TYPE_PPM_ASCII,
    PNM_TYPE_PBM_BINARY, 
    PNM_TYPE_PGM_BINARY,
    PNM_TYPE_PPM_BINARY,
    PNM_TYPE_ERROR
} PNM_TYPE;

/* 画像データ */
typedef struct {
    unsigned int width; /* 画像の横サイズ */
    unsigned int height; /* 画像の縦サイズ */
    unsigned int num_bit; /* 1ピクセルあたりのビット数 */
    unsigned int max_value; /* 最大輝度値 */
    unsigned char *data; /* 画像データの先頭アドレス */
} IMAGE;

/* 関数のプロトタイプ宣言 */
int readPnm(IMAGE*, char*);
int readP1(IMAGE *, char *, unsigned int);
int readP2(IMAGE *, char *, unsigned int);
int readP3(IMAGE *, char *, unsigned int);
int readP4(IMAGE *, char *, unsigned int);
int readP5(IMAGE *, char *, unsigned int);
int readP6(IMAGE *, char *, unsigned int);
int writePnm(IMAGE*, char*, PNM_TYPE);
int writeP1(IMAGE *, FILE *);
int writeP2(IMAGE *, FILE *);
int writeP3(IMAGE *, FILE *);
int writeP4(IMAGE *, FILE *);
int writeP5(IMAGE *, FILE *);
int writeP6(IMAGE *, FILE *);

PNM_TYPE getPnmType(char, char);
unsigned int getImageInfo(IMAGE *, char *, unsigned int, PNM_TYPE);
int toPPM(IMAGE *, IMAGE *);
int toPGM(IMAGE *, IMAGE *);
int toPBM(IMAGE *, IMAGE *);
int allocImage(IMAGE *);
void freeImage(IMAGE *);
char * readFileData(char *, unsigned int *);
unsigned int getNextValue(unsigned int *, char *, unsigned int, unsigned int);
unsigned char bitSwap(unsigned char);

/**
 * バイトの中のビットの並びを逆順にする
 * 
 * 引数
 * byte: バイトデータ
 * 
 * 返却値
 * ビット並び順を変更したバイトデータ
 */
unsigned char bitSwap(unsigned char byte) {
    unsigned char retByte = 0;

    unsigned int i;
    for (i = 0; i < 8; i++) {
        unsigned char bit;

        bit = (byte & (1 << i)) >> i;
        retByte += bit << (8 - i - 1);
    }

    return retByte;
}

/**
 * PNMのタイプを取得する
 * 
 * 引数
 * chr1: マジックナンバーの1文字目
 * chr2: マジックナンバーの2文字目
 * 
 * 返却値
 * PNM_TYPE
 */
PNM_TYPE getPnmType(char chr1, char chr2) {
    PNM_TYPE type;

    /* 1文字目がPでなければPNMではない */
    if (chr1 != 'P') {
        return PNM_TYPE_ERROR;
    }

    /* 2文字目の値に応じてPNMタイプ決定 */
    switch(chr2) {
        case '1':
            type = PNM_TYPE_PBM_ASCII;
            break;
        case '2':
            type = PNM_TYPE_PGM_ASCII;
            break;
        case '3':
            type = PNM_TYPE_PPM_ASCII;
            break;
        case '4':
            type = PNM_TYPE_PBM_BINARY;
            break;
        case '5':
            type = PNM_TYPE_PGM_BINARY;
            break;
        case '6':
            type = PNM_TYPE_PPM_BINARY;
            break;
        default:
            type = PNM_TYPE_ERROR;
            break;
    }
    return type;
}

/**
 * PPM形式に画像を変換する
 * 
 * 引数
 * output: 変換後の画像データ
 * input: 変換したい画像データ
 * 
 * 返却値
 * 成功時: 0, 失敗時: -1
 */
int toPPM(IMAGE *output, IMAGE *input) {
    unsigned int i, j;
    unsigned char g;
    unsigned int num_byte;
    unsigned int num_bit;
    unsigned char bit;
    unsigned int line_byte;

    /* メンバの情報をとりあえずコピー */
    memcpy(output, input, sizeof(IMAGE));

    /* ビット数だけ変更 */
    output->num_bit = 24;

    /* 変換後のデータを格納するメモリを確保 */
    line_byte = (output->width * output->num_bit + 7) / 8;
    output->data = (unsigned char *)malloc(sizeof(unsigned char) * line_byte * output->height);
    if (output->data == NULL) {
        printf("mallocに失敗しました\n");
        return -1;
    }

    /* 既にPPM形式の場合はコピーして終わり */
    if (input->num_bit == 24) {
        memcpy(output->data, input->data, sizeof(unsigned char) * line_byte * output->height);
    } else if (input->num_bit == 1) {
        num_byte = 0;
        num_bit = 0;
        /* 最大輝度値を255に設定 */
        output->max_value = 255;
        for (j = 0; j < input->height; j++) {
            for (i = 0; i < input->width; i++) {
                bit = (input->data[num_byte] & (1 << num_bit)) >> num_bit;
                if (bit == 1) {
                    g = 0;
                } else {
                    g = 255;
                }
                output->data[(j * input->width + i) * 3] = g;
                output->data[(j * input->width + i) * 3 + 1] = g;
                output->data[(j * input->width + i) * 3 + 2] = g;

                num_bit++;

                if (num_bit >= 8) {
                    num_bit = 0;
                    num_byte++;
                }
            }
            if (num_bit != 0) {
                num_bit = 0;
                num_byte++;
            }
        }

    } else if (input->num_bit == 8) {
        for (j = 0; j < input->height; j++) {
            for (i = 0; i < input->width; i++) {
                output->data[(j * input->width + i) * 3 + 0] = input->data[j * input->width + i];
                output->data[(j * input->width + i) * 3 + 1] = input->data[j * input->width + i];
                output->data[(j * input->width + i) * 3 + 2] = input->data[j * input->width + i];
            }
        }
    }

    return 0;
}

/**
 * PGM形式に画像を変換する
 * 
 * 引数
 * output: 変換後の画像データ
 * input: 変換したい画像データ
 * 
 * 返却値
 * 成功時: 0, 失敗時: -1
 */
int toPGM(IMAGE *output, IMAGE *input) {
    unsigned int i, j;
    unsigned char g;
    unsigned int num_byte;
    unsigned int num_bit;
    unsigned char bit;
    unsigned int line_byte;

    /* メンバの情報をとりあえずコピー */
    memcpy(output, input, sizeof(IMAGE));

    /* ビット数だけ変更 */
    output->num_bit = 8;

    /* 変換後のデータを格納するメモリを確保 */
    line_byte = (output->width * output->num_bit + 7) / 8;
    output->data = (unsigned char *)malloc(sizeof(unsigned char) * line_byte * output->height);
    if (output->data == NULL) {
        printf("mallocに失敗しました\n");
        return -1;
    }

    /* 既にPPM形式の場合はコピーして終わり */
    if (input->num_bit == 8) {
        memcpy(output->data, input->data, sizeof(unsigned char) * line_byte * output->height);
    } else if (input->num_bit == 1) {
        num_byte = 0;
        num_bit = 0;
        /* 最大輝度値を255に設定 */
        output->max_value = 255;
        for (j = 0; j < input->height; j++) {
            for (i = 0; i < input->width; i++) {
                bit = (input->data[num_byte] & (1 << num_bit)) >> num_bit;
                if (bit == 1) {
                    g = 0;
                } else {
                    g = 255;
                }
                output->data[j * input->width + i] = g;

                num_bit++;

                if (num_bit >= 8) {
                    num_bit = 0;
                    num_byte++;
                }
            }
            if (num_bit != 0) {
                num_bit = 0;
                num_byte++;
            }
        }

    } else if (input->num_bit == 24) {
        for (j = 0; j < input->height; j++) {
            for (i = 0; i < input->width; i++) {
                output->data[j * input->width + i] = (
                    input->data[(j * input->width + i) * 3]
                    + input->data[(j * input->width + i) * 3 + 1]
                    + input->data[(j * input->width + i) * 3 + 2]) / 3;
            }
        }
    }

    return 0;
}

/**
 * PBM形式に画像を変換する
 * 
 * 引数
 * output: 変換後の画像データ
 * input: 変換したい画像データ
 * 
 * 返却値
 * 成功時: 0, 失敗時: -1
 */
int toPBM(IMAGE *output, IMAGE *input) {
    unsigned int i, j;
    unsigned char g;
    unsigned int num_byte;
    unsigned int num_bit;
    unsigned int line_byte;
    unsigned char byte_data;

    /* メンバの情報をとりあえずコピー */
    memcpy(output, input, sizeof(IMAGE));

    /* ビット数だけ変更 */
    output->num_bit = 1;

    /* 変換後のデータを格納するメモリを確保 */
    line_byte = (output->width * output->num_bit + 7) / 8;
    output->data = (unsigned char *)malloc(sizeof(unsigned char) * line_byte * output->height);
    if (output->data == NULL) {
        printf("mallocに失敗しました\n");
        return -1;
    }

    /* 既にPPM形式の場合はコピーして終わり */
    if (input->num_bit == 1) {
        memcpy(output->data, input->data, sizeof(unsigned char) * line_byte * output->height);
    } else if (input->num_bit == 8) {
        num_byte = 0;
        num_bit = 0;
        byte_data = 0;
        for (j = 0; j < input->height; j++) {
            for (i = 0; i < input->width; i++) {
                if (input->data[j * input->width + i] < 128) {
                    byte_data += (1 << num_bit);
                }
                num_bit++;

                if (num_bit >= 8) {
                    output->data[num_byte] = byte_data;
                    num_bit = 0;
                    byte_data = 0;
                    num_byte++;
                }
            }
            if (num_bit != 0) {
                output->data[num_byte] = byte_data;
                num_bit = 0;
                byte_data = 0;
                num_byte++;
            }
        }

    } else if (input->num_bit == 24) {
        num_byte = 0;
        num_bit = 0;
        byte_data = 0;
        for (j = 0; j < input->height; j++) {
            for (i = 0; i < input->width; i++) {
                g = (input->data[(j * input->width + i) * 3]
                    + input->data[(j * input->width + i) * 3 + 1]
                    + input->data[(j * input->width + i) * 3 + 2]) / 3;

                if (g < 128) {
                    byte_data += (1 << num_bit);
                }
                num_bit++;

                if (num_bit >= 8) {
                    output->data[num_byte] = byte_data;
                    num_bit = 0;
                    byte_data = 0;
                    num_byte++;
                }
            }
            if (num_bit != 0) {
                output->data[num_byte] = byte_data;
                num_bit = 0;
                byte_data = 0;
                num_byte++;
            }
        }
    }

    return 0;
}

/**
 * P1ファイルをファイル保存
 * 
 * 引数
 * image: 保存する画像の情報
 * fp: 保存先ファイルへのファイルポインタ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int writeP1(IMAGE *image, FILE *fp) {

    unsigned int num_bit;
    unsigned int num_byte;
    unsigned int num_output;
    unsigned char bit_data;
    unsigned char byte_data;
    unsigned int i, j;

    /* ヘッダーに情報書き出し */
    fprintf(fp, "P1\n");
    fprintf(fp, "%u %u\n", image->width, image->height);

    num_byte = 0;
    num_bit = 0;
    num_output = 0;

    /* 全輝度値をファイルに書き出し */
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < image->width; i++) {
            byte_data = image->data[num_byte];
            bit_data = (byte_data & (1 << num_bit)) >> num_bit;

            /* ASCII形式でファイルに書き出し */
            fprintf(fp, "%u ", bit_data);
            num_output++;
            num_bit++;

            /* 適度に改行を入れておく */
            if (num_output % 60 == 59) {
                fprintf(fp, "\n");
            }

            if (num_bit >= 8) {
                num_byte++;
                num_bit = 0;
            }
        }
        if (num_bit != 0) {
            num_bit = 0;
            num_byte++;
        }
    }

    return 0;
}

/**
 * P2ファイルをファイル保存
 * 
 * 引数
 * image: 保存する画像の情報
 * fp: 保存先ファイルへのファイルポインタ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int writeP2(IMAGE *image, FILE *fp) {
    
    unsigned int num_data;
    unsigned int line_byte;

    /* ヘッダーに情報書き出し */
    fprintf(fp, "P2\n");
    fprintf(fp, "%u %u\n", image->width, image->height);
    fprintf(fp, "%u\n", image->max_value);

    line_byte = (image->width * image->num_bit + 7) / 8;
    num_data = 0;

    /* 全輝度値をファイルに書き出し */
    while (num_data < line_byte * image->height) {
        /* ASCII形式でファイルに書き出し */
        fprintf(fp, "%u ", image->data[num_data]);

        /* 適度に改行を入れておく */
        if (num_data % 20 == 19) {
            fprintf(fp, "\n");
        }

        /* 書き出した輝度値の数をインクリメント */
        num_data += 1;
    }

    return 0;
}

/**
 * P3ファイルをファイル保存
 * 
 * 引数
 * image: 保存する画像の情報
 * fp: 保存先ファイルへのファイルポインタ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int writeP3(IMAGE *image, FILE *fp) {
    
    unsigned int num_data;
    unsigned int line_byte;

    /* ヘッダーに情報書き出し */
    fprintf(fp, "P3\n");
    fprintf(fp, "%u %u\n", image->width, image->height);
    fprintf(fp, "%u\n", image->max_value);

    line_byte = (image->width * image->num_bit + 7) / 8;
    num_data = 0;

    /* 全輝度値をファイルに書き出し */
    while (num_data < line_byte * image->height) {
        /* ASCII形式でファイルに書き出し */
        fprintf(fp, "%u ", image->data[num_data]);

        /* 適度に改行を入れておく */
        if (num_data % 20 == 19) {
            fprintf(fp, "\n");
        }

        /* 書き出した輝度値の数をインクリメント */
        num_data += 1;
    }

    return 0;
}

/**
 * P4ファイルをファイル保存
 * 
 * 引数
 * image: 保存する画像の情報
 * fp: 保存先ファイルへのファイルポインタ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int writeP4(IMAGE *image, FILE *fp) {
    
    unsigned int num_data;
    unsigned int line_byte;

    /* ヘッダーに情報書き出し */
    fprintf(fp, "P4\n");
    fprintf(fp, "%u %u\n", image->width, image->height);

    line_byte = (image->width * image->num_bit + 7) / 8;
    num_data = 0;

    /* 全輝度値をファイルに書き出し */
    while (num_data < line_byte * image->height) {

        /* バイナリ形式でファイルに書き出し */
        fputc(bitSwap(image->data[num_data]), fp);

        /* 書き出した輝度値の数をインクリメント */
        num_data += 1;
    }
    
    return 0;
}

/**
 * P5ファイルをファイル保存
 * 
 * 引数
 * image: 保存する画像の情報
 * fp: 保存先ファイルへのファイルポインタ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int writeP5(IMAGE *image, FILE *fp) {
    
    unsigned int num_data;
    unsigned int line_byte;

    /* ヘッダーに情報書き出し */
    fprintf(fp, "P5\n");
    fprintf(fp, "%u %u\n", image->width, image->height);
    fprintf(fp, "%u\n", image->max_value);

    line_byte = (image->width * image->num_bit + 7) / 8;
    num_data = 0;

    /* 全輝度値をファイルに書き出し */
    while (num_data < line_byte * image->height) {

        /* バイナリ形式でファイルに書き出し */
        fputc(image->data[num_data], fp);

        /* 書き出した輝度値の数をインクリメント */
        num_data += 1;
    }

    return 0;
}

/**
 * P6ファイルをファイル保存
 * 
 * 引数
 * image: 保存する画像の情報
 * fp: 保存先ファイルへのファイルポインタ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int writeP6(IMAGE *image, FILE *fp) {
    
    unsigned int num_data;
    unsigned int line_byte;

    /* ヘッダーに情報書き出し */
    fprintf(fp, "P6\n");
    fprintf(fp, "%u %u\n", image->width, image->height);
    fprintf(fp, "%u\n", image->max_value);

    line_byte = (image->width * image->num_bit + 7) / 8;
    num_data = 0;

    /* 全輝度値をファイルに書き出し */
    while (num_data < line_byte * image->height) {

        /* バイナリ形式でファイルに書き出し */
        fputc(image->data[num_data], fp);

        /* 書き出した輝度値の数をインクリメント */
        num_data += 1;
    }

    return 0;
}

/**
 * PNMの画像データをファイル保存する
 * 
 * image: 保存する画像データ構造体
 * file_name: ファイル名
 * type: PNMのタイプ
 */
int writePnm(IMAGE *image, char *file_name, PNM_TYPE type) {
    int ret;
    IMAGE conv_image;

    /* ファイルをオープン */
    FILE *fp = fopen(file_name, "w");
    if (fp == NULL) {
        printf("%sが読み込めません\n", file_name);
        return -1;
    }

    /* タイプに応じて書き込む関数を呼び分ける */
    switch (type) {
        case PNM_TYPE_PBM_ASCII:
            toPBM(&conv_image, image);
            ret = writeP1(&conv_image, fp);
            break;
        case PNM_TYPE_PGM_ASCII:
            toPGM(&conv_image, image);
            ret = writeP2(&conv_image, fp);
            break;
        case PNM_TYPE_PPM_ASCII:
            toPPM(&conv_image, image);
            ret = writeP3(&conv_image, fp);
            break;
        case PNM_TYPE_PBM_BINARY:
            toPBM(&conv_image, image);
            ret = writeP4(&conv_image, fp);
            break;
        case PNM_TYPE_PGM_BINARY:
            toPGM(&conv_image, image);
            ret = writeP5(&conv_image, fp);
            break;
        case PNM_TYPE_PPM_BINARY:
            toPPM(&conv_image, image);
            ret = writeP6(&conv_image, fp);
            break;
        default:
            printf("このPNMタイプはサポートしていません\n");
            ret = 1;
            break;
    }

    fclose(fp);

    return ret;
}

/**
 * ファイルデータの次の文字列を数値化して取得する
 * 
 * 引数
 * value: 数値化した結果
 * file: ファイルデータの先頭アドレス
 * read_pos: 読み込み位置
 * file_size: ファイルデータのサイズ
 * 
 * 返却値
 * 成功: ファイルデータから読み込んだサイズ
 * 失敗: 0
 */
unsigned int getNextValue(unsigned int *value, char *data, unsigned int read_pos, unsigned int file_size) {
    char str[256];

    /* 空白系の文字やコメントを除いた次の文字列を取得する */
    unsigned int i, j, k;
    
    i = 0;
    while (read_pos + i < file_size) {
        /* 空白系の文字の場合は次の文字へスキップ */
        if (isspace(data[read_pos + i])) {
            i++;
            continue;
        }

        /* #ならそれ以降はコメントなので次の行へ */
        if (data[read_pos + i] == '#') {
            do {
                i++;
            } while (read_pos + i < file_size && data[read_pos + i] != '\n');

            /* \nの1文字文進める */
            i++;
        }

        break;
    }

    /* 文字列を取得 */
    j = 0;
    while (read_pos + i + j < file_size && !isspace(data[read_pos + i + j])) {
        /* 読み込んだバイト数をカウントアップ */
        j++;
    }

    /* 文字列を数字に変換 */
    for (k = 0; k < j; k++) {
        str[k] = data[read_pos + i + k];
    }
    str[k] = '\0';

    /* int化 */
    *value = (unsigned int)atoi(str);

    /* 読み込んだ文字数を返却 */
    return (i + j);
}

/**
 * ヘッダーを読み込み結果をIMAGE構造体に格納する
 * 
 * 引数
 * image: 画像データ格納用の構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size: ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 画像データの先頭位置
 * 失敗: 0
 */
unsigned int getImageInfo(IMAGE *image, char *file_data, unsigned int file_size, PNM_TYPE type) {

    unsigned int read_pos;
    unsigned int value;
    unsigned int read_size;

    /* データ読み込み位置を先頭にセット */
    read_pos = 0;

    /* マジックナンバー分を読み飛ばす */
    read_pos += 2;

    /* 画像の横サイズを取得する */
    read_size = getNextValue(&value, file_data, read_pos, file_size);
    image->width = value;

    read_pos += read_size;

    /* 画像の縦サイズを取得する */
    read_size = getNextValue(&value, file_data, read_pos, file_size);
    image->height = value;

    read_pos += read_size;

    /* 画像の最大輝度値を取得する */
    switch (type) {
        case PNM_TYPE_PGM_ASCII:
        case PNM_TYPE_PPM_ASCII:
        case PNM_TYPE_PGM_BINARY:
        case PNM_TYPE_PPM_BINARY:
            /* 取得するのはPGMとPBMのみ */
            read_size = getNextValue(&value, file_data, read_pos, file_size);

            /* 最大輝度値の値チェック */
            if (value > MAX_VALUE) {
                printf("最大輝度値が不正です\n");
                return 0;
            }

            image->max_value = value;
            read_pos += read_size;
            break;
        default:
            break;
    }

    /* PNMタイプに応じてピクセルあたりのバイト数を設定 */
    switch (type) {
        case PNM_TYPE_PBM_ASCII:
        case PNM_TYPE_PBM_BINARY:
            image->num_bit = 1;
            break;
        case PNM_TYPE_PGM_ASCII:
        case PNM_TYPE_PGM_BINARY:
            image->num_bit = 8;
            break;
        case PNM_TYPE_PPM_ASCII:
        case PNM_TYPE_PPM_BINARY:
            image->num_bit = 24;
            break;
        default:
            break;
    }

    return read_pos;
}

/**
 * 画像データ格納用のバッファを確保する
 * 
 * 引数
 * image: 画像データ格納用の構造体
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int allocImage(IMAGE *image) {
    unsigned int size;
    unsigned char *data;
    unsigned int line_byte;

    if (image == NULL) {
        return -1;
    }

    /* 1行あたりのバイト数を計算(切り上げ) */
    line_byte = (image->width * image->num_bit + 7) / 8;

    /* サイズを決定してメモリ取得 */
    size = line_byte * image->height;
    data = (unsigned char *)malloc(sizeof(unsigned char) * size);
    if (data == NULL) {
        printf("mallocに失敗しました\n");
        return -1;
    }

    /* 取得したメモリのアドレスをimage構造体にセット */
    image->data = data;

    return 0;

}

/**
 * P1ファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size; ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int readP1(IMAGE *image, char *file_data, unsigned int file_size) {
    unsigned int read_pos;
    unsigned int value;
    unsigned int num_byte;
    unsigned char byte;
    unsigned int num_bit;
    unsigned int i, j;
 
    /* ヘッダーを読み込んでImage構造体にデータをつめる */
    read_pos = getImageInfo(image, file_data, file_size, PNM_TYPE_PBM_ASCII);

    if (read_pos == 0) {
        /* ヘッダー読み込みに失敗していたら終了 */
        printf("ヘッダーがおかしいです\n");
        return -1;
    }

    /* ヘッダーの情報に基づいてメモリ確保 */
    if (allocImage(image) != 0) {
        printf("メモリ取得に失敗しました\n");
        return -1;
    }

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    num_bit = 0;
    byte = 0;
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < image->width; i++) {

            unsigned int read_size;

            /* 次の値をファイルデータから取得 */
            read_size = getNextValue(&value, file_data, read_pos, file_size);

            /* ビットをバイトに詰める */
            byte += value << num_bit;
            num_bit++;
            if (num_bit >= 8) {
                /* 1バイト分詰め込んだらimage構造体に格納 */
                image->data[num_byte] = byte;
                byte = 0;

                /* 次のバイトにビットを詰め込んでいく */
                num_bit = 0;
                num_byte++;
            }


            /* データ読み込み位置と読み込んだデータ数を計算 */
            read_pos += read_size;
                
            /* ファイルサイズ分読み込んでいたら終了 */
            if (read_pos >= file_size) {
                return 0;
            }
        }
        if (num_bit != 0) {
            /* 次の行は最下位ビットからバイトに詰めていく */
            image->data[num_byte] = byte;
            byte = 0;
            num_bit = 0;
            num_byte++;
        }
    }

    return 0;
}

/**
 * P2ファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size; ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int readP2(IMAGE *image, char *file_data, unsigned int file_size) {
    unsigned int read_pos;
    unsigned int value;
    unsigned int num_byte;
    unsigned int i, j, c;
    unsigned int color;
 
    /* ヘッダーを読み込んでImage構造体にデータをつめる */
    read_pos = getImageInfo(image, file_data, file_size, PNM_TYPE_PGM_ASCII);

    if (read_pos == 0) {
        /* ヘッダー読み込みに失敗していたら終了 */
        printf("ヘッダーがおかしいです\n");
        return -1;
    }

    /* ヘッダーの情報に基づいてメモリ確保 */
    if (allocImage(image) != 0) {
        printf("メモリ取得に失敗しました\n");
        return -1;
    }

    /* 1ピクセルあたりの色の数を設定 */
    color = image->num_bit / 8;

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < image->width; i++) {
            for (c = 0; c < color; c++) {
                unsigned int read_size;

                /* 次の値をファイルデータから取得 */
                read_size = getNextValue(&value, file_data, read_pos, file_size);

                /* 輝度値をIMAGE構造体に格納 */
                if (value > image->max_value) {

                    /* 最大輝度値を超える場合 */
                    image->data[num_byte] = (unsigned char)image->max_value;
                } else {
                    image->data[num_byte] = (unsigned char)value;
                }

                /* 格納したデータ数をインクリメント */
                num_byte += 1;

                /* データ読み込み位置と読み込んだデータ数を計算 */
                read_pos += read_size;
                
                /* ファイルサイズ分読み込んでいたら終了 */
                if (read_pos >= file_size) {
                    return 0;
                }
            }
        }
    }

    return 0;
}

/**
 * P3ファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size; ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int readP3(IMAGE *image, char *file_data, unsigned int file_size) {
    unsigned int read_pos;
    unsigned int value;
    unsigned int num_byte;
    unsigned int i, j, c;
    unsigned int color;
 
    /* ヘッダーを読み込んでImage構造体にデータをつめる */
    read_pos = getImageInfo(image, file_data, file_size, PNM_TYPE_PPM_ASCII);

    if (read_pos == 0) {
        /* ヘッダー読み込みに失敗していたら終了 */
        printf("ヘッダーがおかしいです\n");
        return -1;
    }

    /* ヘッダーの情報に基づいてメモリ確保 */
    if (allocImage(image) != 0) {
        printf("メモリ取得に失敗しました\n");
        return -1;
    }

    /* 1ピクセルあたりの色の数を設定 */
    color = image->num_bit / 8;

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < image->width; i++) {
            for (c = 0; c < color; c++) {
                unsigned int read_size;

                /* 次の値をファイルデータから取得 */
                read_size = getNextValue(&value, file_data, read_pos, file_size);

                /* 輝度値をIMAGE構造体に格納 */
                if (value > image->max_value) {

                    /* 最大輝度値を超える場合 */
                    image->data[num_byte] = (unsigned char)image->max_value;
                } else {
                    image->data[num_byte] = (unsigned char)value;
                }

                /* 格納したデータ数をインクリメント */
                num_byte += 1;

                /* データ読み込み位置と読み込んだデータ数を計算 */
                read_pos += read_size;
                
                /* ファイルサイズ分読み込んでいたら終了 */
                if (read_pos >= file_size) {
                    return 0;
                }
            }
        }
    }

    return 0;
}

/**
 * P4ファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size; ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 * 
 */
int readP4(IMAGE *image, char *file_data, unsigned int file_size) {
    unsigned int read_pos;
    unsigned char byte_data;
    unsigned int num_byte;
    unsigned int i, j;
 
    /* ヘッダーを読み込んでImage構造体にデータをつめる */
    read_pos = getImageInfo(image, file_data, file_size, PNM_TYPE_PBM_BINARY);

    if (read_pos == 0) {
        /* ヘッダー読み込みに失敗していたら終了 */
        printf("ヘッダーがおかしいです\n");
        return -1;
    }

    /* ヘッダーの情報に基づいてメモリ確保 */
    if (allocImage(image) != 0) {
        printf("メモリ取得に失敗しました\n");
        return -1;
    }

    /* 画像の縦サイズの次にある空白系文字の分、読み込み位置を加算 */
    read_pos += 1;

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    
    /* ファイルサイズ分読み込んでいたら終了 */
    if (read_pos >= file_size) {
        return 0;
    }

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < (image->width + 7) / 8; i++) {

            /* 輝度値をIMAGE構造体に格納 */
            byte_data = bitSwap((unsigned char)file_data[read_pos]);
            image->data[num_byte] = byte_data;

            /* 格納したデータ数をインクリメント */
            num_byte += 1;

            /* データ読み込み位置と読み込んだデータ数を計算 */
            read_pos += 1;
            
            /* ファイルサイズ分読み込んでいたら終了 */
            if (read_pos >= file_size) {
                return 0;
            }
        }
    }

    return 0;
}

/**
 * P5ファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size; ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 * 
 */
int readP5(IMAGE *image, char *file_data, unsigned int file_size) {
    unsigned int read_pos;
    unsigned int num_byte;
    unsigned int i, j, c;
    unsigned char byte_data;
    unsigned int color;
 
    /* ヘッダーを読み込んでImage構造体にデータをつめる */
    read_pos = getImageInfo(image, file_data, file_size, PNM_TYPE_PGM_BINARY);

    if (read_pos == 0) {
        /* ヘッダー読み込みに失敗していたら終了 */
        printf("ヘッダーがおかしいです\n");
        return -1;
    }

    /* ヘッダーの情報に基づいてメモリ確保 */
    if (allocImage(image) != 0) {
        printf("メモリ取得に失敗しました\n");
        return -1;
    }

    /* 最大輝度値の次にある空白系文字の分、読み込み位置を加算 */
    read_pos += 1;

    /* 1ピクセルあたりの色の数を設定 */
    color = image->num_bit / 8;

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < image->width; i++) {
            for (c = 0; c < color; c++) {

                /* 輝度値をIMAGE構造体に格納 */
                byte_data = (unsigned char)file_data[read_pos];
                image->data[num_byte] = byte_data;

                /* 格納したデータ数をインクリメント */
                num_byte += 1;

                /* データ読み込み位置と読み込んだデータ数を計算 */
                read_pos += 1;
                
                /* ファイルサイズ分読み込んでいたら終了 */
                if (read_pos >= file_size) {
                    return 0;
                }
            }
        }
    }

    return 0;
}

/**
 * P6ファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_data: ファイルデータの先頭アドレス
 * file_size; ファイルデータのサイズ
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 * 
 */
int readP6(IMAGE *image, char *file_data, unsigned int file_size) {
    unsigned int read_pos;
    unsigned int num_byte;
    unsigned int i, j, c;
    unsigned char byte_data;
    unsigned int color;
 
    /* ヘッダーを読み込んでImage構造体にデータをつめる */
    read_pos = getImageInfo(image, file_data, file_size, PNM_TYPE_PPM_BINARY);

    if (read_pos == 0) {
        /* ヘッダー読み込みに失敗していたら終了 */
        printf("ヘッダーがおかしいです\n");
        return -1;
    }

    /* ヘッダーの情報に基づいてメモリ確保 */
    if (allocImage(image) != 0) {
        printf("メモリ取得に失敗しました\n");
        return -1;
    }

    /* 最大輝度値の次にある空白系文字の分、読み込み位置を加算 */
    read_pos += 1;

    /* 1ピクセルあたりの色の数を設定 */
    color = image->num_bit / 8;

    /* ファイル全体を読み終わるか必要な数分の輝度数をセットするまでループ */
    num_byte = 0;
    for (j = 0; j < image->height; j++) {
        for (i = 0; i < image->width; i++) {
            for (c = 0; c < color; c++) {

                /* 輝度値をIMAGE構造体に格納 */
                byte_data = (unsigned char)file_data[read_pos];
                image->data[num_byte] = byte_data;

                /* 格納したデータ数をインクリメント */
                num_byte += 1;

                /* データ読み込み位置と読み込んだデータ数を計算 */
                read_pos += 1;
                
                /* ファイルサイズ分読み込んでいたら終了 */
                if (read_pos >= file_size) {
                    return 0;
                }
            }
        }
    }

    return 0;
}

/**
 * PNMファイルのヘッダーと画像データをIMAGE構造体に格納する
 * 
 * 引数
 * image: 読み込んだ画像データ構造体
 * file_name: 読み込むファイルのパス
 * 
 * 返却値
 * 成功: 0
 * 失敗: 0以外
 */
int readPnm(IMAGE *image, char *file_name) {
    int ret;
    char *file_data;
    unsigned int file_size;
    PNM_TYPE type;

    /* ファイル全体のデータを読み込む */
    file_data = readFileData(file_name, &file_size);
    if (file_data == NULL) {
        printf("ファイルデータの読み込みに失敗しました\n");
        return -1;
    }

    /* マジックナンバー(PNMのタイプ)を取得 */
    type = getPnmType(file_data[0], file_data[1]);

    /* PNMファイルを読み込んでデータをimageに格納 */
    switch (type) {
        case PNM_TYPE_PBM_ASCII:
            ret = readP1(image, file_data, file_size);
            break;
        case PNM_TYPE_PGM_ASCII:
            ret = readP2(image, file_data, file_size);
            break;
        case PNM_TYPE_PPM_ASCII:
            ret = readP3(image, file_data, file_size);
            break;
        case PNM_TYPE_PBM_BINARY:
            ret = readP4(image, file_data, file_size);
            break;
        case PNM_TYPE_PGM_BINARY:
            ret = readP5(image, file_data, file_size);
            break;
        case PNM_TYPE_PPM_BINARY:
            ret = readP6(image, file_data, file_size);
            break;
        default:
            printf("マジックナンバーが不正です\n");
            ret = -1;
            break;
    }

    free(file_data);

    return ret;
}

/**
 * PNMファイルを読み込む
 * 
 * 引数
 * file_name: 読み込むファイルのパス
 * file_size: 読み込んだファイルのサイズ
 * 
 * 返却値
 * 成功: 読み込んだファイルデータの先頭アドレス
 * 失敗: NULL
 * 
 */
char * readFileData(char *file_name, unsigned int *file_size) {
    char *file_data = NULL;
    size_t read_size;
    unsigned int size;
    char tmp[256];
    FILE *fp;

    /* ファイルサイズ取得用にオープン */
    fp = fopen(file_name, "r");
    if (fp == NULL) {
        printf("%sが読み込めません\n", file_name);
        return NULL;
    }

    /* ファイルのサイズを取得 */
    size = 0;
    do {
        read_size = fread(tmp, 1, 256, fp);
        size += read_size;
    } while (read_size == 256);

    /* ファイルを一旦クローズ */
    fclose(fp);

    /* ファイルデータ読み込み用にオープン */
    fp = fopen(file_name, "r");
    if (fp == NULL) {
        printf("%sが読み込めません\n", file_name);
        return NULL;
    }

    /* ファイルデータ読み込み用のメモリ確保 */
    file_data = (char*)malloc(sizeof(char) * size);
    if (file_data == NULL) {
        printf("メモリが取得できません\n");
        fclose(fp);
        return NULL;
    }

    /* データの読み込み */
    read_size = fread(file_data, 1, size, fp);

    fclose(fp);

    if (read_size != size) {
        printf("読み込みサイズがおかしいです\n");
        free(file_data);
        return NULL;
    }

    /* サイズを設定 */
    *file_size = (unsigned int)size;

    /* 読み込んだデータの先頭アドレス返却 */
    return file_data;
}

/**
 * 画像データ構造体の画像データを解放する
 * 
 * 引数
 * image: 解放する画像データ構造体
 */
void freeImage(IMAGE *image) {
    if (image != NULL) {
        free(image->data);
    }
}

/* main関数 */
int main(int argc, char *argv[]) {
    IMAGE input_image;
    int number;
    char file_name[256];
    PNM_TYPE type;
    
    if (argc < 3) {
        printf("引数に読み込みたいファイル名と数字を指定してください\n");
        return -1;
    }

    strcpy(file_name, argv[1]);
    number = atoi(argv[2]);

    /* PNMの画像データを読み込む */
    if (readPnm(&input_image, file_name) != 0) {
        return -1;
    }

    switch (number) {
        case 1:
            type = PNM_TYPE_PBM_ASCII;
            break;
        case 2:
            type = PNM_TYPE_PGM_ASCII;
            break;
        case 3:
            type = PNM_TYPE_PPM_ASCII;
            break;
        case 4:
            type = PNM_TYPE_PBM_BINARY;
            break;
        case 5:
            type = PNM_TYPE_PGM_BINARY;
            break;
        case 6:
            type = PNM_TYPE_PPM_BINARY;
            break;
        default:
            type = PNM_TYPE_PPM_ASCII;
            break;
    }

    /* 画像処理後の画像データをPNMとして保存 */
    if (writePnm(&input_image, "output.ppm", type)) {
        freeImage(&input_image);
        return -1;
    }

    /* 画像データを解放する */
    freeImage(&input_image);

    return 0;
}

スポンサーリンク

実行方法

プログラム実行時には引数に「入力ファイル名」と「マジックナンバーの数字」を指定してください。

入力ファイル名で指定した PNM ファイルを、指定したマジックナンバーの画像に変換して output.ppm としてファイル書き出しされます。

例えば下記のように実行すれば、”input.ppm” が P1 形式の “output.ppm” としてファイル書き込みされます。

> プログラム名 input.ppm 1

解説

下記の関数については、

  • readP1
  • readP2
  • readP3
  • readP4
  • readP5
  • readP6
  • writeP1
  • writeP2
  • writeP3
  • writeP4
  • writeP5
  • writeP6

下記ページで紹介していますのでコチラを参考にしていただければと思います。

pnmの読み込み書き出し方法解説ページのアイキャッチ【C言語/画像処理】PNM(PPM・PGM・PBM)ファイルの読み込みと書き出し【ライブラリ不要】

ただし、上記の wirte 系の関数は、入力される画像がファイル書き出しを行うフォーマットに合ったものでないと動作しないようになっています。

例えば writeP6 では、引数で指定する IMAGE 構造体が PPM フォーマットに合ったもので無いと上手く書き出しできません。

そのため、writePnm 関数の中で、write 系の関数を実行する前に、その関数で書き出しを行うフォーマットに合わせて画像(IMAGE 構造体)のフォーマットの変換を行うようにしています。

フォーマットの変換を行っているのは下記の3つの関数です。

  • toPPM:PPM に変換する関数
  • toPGM:PGM に変換する関数
  • toPBM:PBM に変換する関数

これらはそれぞれで1ピクセルあたりのビット数が異なるので、変換先のフォーマットに合わせてそのビット数を変更し、さらにそのビット数に合わせて画像の変換を行っています。

例えば toPGM では、入力される画像フォーマットに応じて下記のように変換を行っています。

  • PPM(24bit -> 8bit):輝度値3つ(RGB)の平均を求めて1つの輝度値(8bit データ)を作成
  • PGM(8bit -> 8bit):そのままコピーするだけ
  • PBM(1bit -> 8bit):”0″ の場合は “255”、”1″ の場合は “0” とした1つの輝度値(8bit データ)を作成

どの画像フォーマットが入力されたかは、getImageInfo 関数でセットされる IMAGE 構造体の num_bit の値から判断しています。

まとめ

このページでは PPM・PGM・PBM のフォーマットをそれぞれ相互変換するプログラムの紹介を行いました。

どちらかというと、このページを公開した目的は下記ページで紹介した PNM ファイルの読み込み・書き込み関数の実際の使用例を示すことでした。

pnmの読み込み書き出し方法解説ページのアイキャッチ【C言語/画像処理】PNM(PPM・PGM・PBM)ファイルの読み込みと書き出し【ライブラリ不要】

ですのでプログラムの解説については簡単に済まさせていただいています。が、疑問点などはコメントいただければ補足させていただきますので、遠慮なく質問していただければと思います。

とはいえ、PNM ファイルのフォーマットを変換するプログラムは、特に PNM ファイルを実際に利用している方や利用してみたい方には役に立つと思いますので、是非このページのプログラムも活用していただければと思います!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です