質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C

    4534questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C言語での画像の縮小について

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,514

shuppi

score 35

すみません;;
このプログラムを何とか「画像を縮小して表示」に直していただけないでしょうか?
画像サイズは800×480でbmpです。

何ビットか飛ばして読み込めば?だかなんだか言われて色々試してみましたが、ズームみたいになったり、画像の一部だけ表示されたりと、まったくうまくいきませんでした。

縮小した画像は保存ではなくただ表示したいだけです。
Ubuntuでやってます。

よろしくお願いします。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

#define    SCREENWIDTH        800
#define    SCREENHEIGHT        480
#define    BYTES_PER_PIXCEL    4
#define    SCREENSIZE        (SCREENWIDTH * SCREENHEIGHT * BYTES_PER_PIXCEL)
#define    RGB888(r, g, b)     (((r) & 0xff) << 16 | \
                         ((g) & 0xff) <<  8 | \
                         ((b) & 0xff))

typedef struct tagBITMAPFILEHEADER{        // ビットマップファイルヘッダ
    unsigned short    bfType;                // 識別子0x4d42(‘B’,‘M’)
    unsigned long    bfSize;                // ファイルサイズ
    unsigned short    bfReserved1;            // 使わない
    unsigned short    bfReserved2;            // 使わない
    unsigned long    bfOffBits;            // ファイル内の画像データ開始位置
} __attribute__((packed)) BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{        // ビットマップ情報ヘッダ
    unsigned long    biSize;                // 情報ヘッダサイズ
    long            biWidth;                // 画像の幅
    long            biHeight;                // 画像の高さ
    unsigned short    biPlanes;                // プレーン数(1に固定)
    unsigned short    biBitCount;            // 1ピクセルあたりのビット数
    unsigned long    biCompression;            // 圧縮タイプ
    unsigned long    biSizeImage;            // 画像データサイズ
    long            biXPixPerMeter;        // 横1mあたりのピクセル数
    long            biYPixPerMeter;        // 縦1mあたりのピクセル数
    unsigned long    biClrUsed;            // パレット数
    unsigned long    biClrImporant;            // 重要パレット数
} __attribute__((packed)) BITMAPINFOHEADER;

/*
 *     draw_bmp - draw bitmap image on LCD screen
 *     pfb: pointer to the framebuffer
 *     x0: x coordinates of image
 *     y0: y coordinates of image
 *     w: image width
 *     h: image height
 *     bmpdata: pointer to the image data
 */
void draw_bmp(unsigned int *pfb, int x0, int y0, int w, int h, unsigned char *bmpdata)
{
    int x1 = x0 + w;
    int y1 = y0 + h;
    int padding = (w * 3) % 4;
    int c = 0;
    unsigned char r, g, b;
    int x, y;

    // 開始位置から画像を表示します。
    // 最も下のラインから始まり、
    // 最も上のラインに向かって1ラインずつ画像データを格納します。
    for(y = y1 - 1; y >= y0; y--){
        for(x = x0; x < x1; x++){
            // 1pixelから、R,G,B各色のカラーデータを取得します。
            b = bmpdata[c++];
            g = bmpdata[c++];
            r = bmpdata[c++];
            // LCDにカラーデータを表示します。
            pfb[y * SCREENWIDTH + x] = RGB888(r, g, b);
        }

        //1ラインのデータサイズが4の倍数にならない時は、
        //4の倍数になるようにラインデータの末尾に1つ以上の0を付加します。
        c += padding;
    }
}

int main(void) {
    int fd_in, fd_fb;
    unsigned int *pfb;
    unsigned char *bmpdata;
    int datasize;
    int x, y;

    struct bmpheader_t{
        BITMAPFILEHEADER fh;
        BITMAPINFOHEADER ih;
    } bmp;


    // 引数に設定されたファイルをオープンします。
    // オープンに失敗した場合はエラーで終了します。
    if ((fd_in = open("hana.bmp", O_RDONLY)) < 0) {
        perror("open(file)");
        return 1;
    }

    // ビットマップヘッダに、画像データを読み込みます。
    // 読み込みに失敗した場合はエラーで終了します。
    if (read(fd_in, &bmp, sizeof(bmp)) != sizeof(bmp)){
        perror("read(file)");
        return 1;
    }

    // 取得した画像データより、
    // 識別子、1ピクセルあたりのビット数、圧縮タイプ、画像の高さをチェックします。
    if (bmp.fh.bfType != 0x4d42 || bmp.ih.biBitCount != 24
        || bmp.ih.biCompression != 0 || bmp.ih.biHeight < 0){
        fprintf(stderr, "unsupported bitmap format\n");
        return 1;
    }

    // 取得した画像データより、
    // 画像データの幅と高さが画面サイズよりも大きい場合はエラーで終了します。
    if (bmp.ih.biWidth > SCREENWIDTH || bmp.ih.biHeight > SCREENHEIGHT){
        fprintf(stderr, "image size too big\n");
        return 1;
    }

    // 画像データから、ビットマップファイルのヘッダ情報のデータサイズを引いた値を
    // データサイズとして、メモリ領域を確保します。
    datasize = bmp.fh.bfSize - sizeof(bmp);
    // 必要なメモリ領域を確保できない場合はエラーで終了します。
    if (!(bmpdata = malloc(datasize))){
        perror("malloc");
        return 1;
    }

    // 確保したメモリ領域に画像データを読み込みます。
    // 読み込みに失敗した場合はエラーで終了します。
    if (read(fd_in, bmpdata, datasize) != datasize){
        perror("read(file)");
        free(bmpdata);
        return 1;
    }

    //画像ファイルをクローズします。
    close(fd_in);

    // フレームバッファをオープンします。
    // オープンに失敗した場合はエラーで終了します。
    if ((fd_fb = open("/dev/fb0", O_RDWR)) < 0) {
        perror("open(fb)");
        free(bmpdata);
        return 1;
    }

    // mmapによりバッファの先頭アドレスを取得します。
    pfb = mmap(0, SCREENSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd_fb, 0);
    // 取得に失敗した場合はエラーで終了します。
    if (pfb == MAP_FAILED){
        perror("mmap");
        free(bmpdata);
        return 1;
    }

    // LCDの画面表示を全て消去するために、
    // 取得したアドレスから確保領域を0で初期化します。
    memset(pfb, 0, SCREENSIZE);

    // 画像がLCDの中央に表示されるようにx, yを設定します。
    x = (SCREENWIDTH - bmp.ih.biWidth) / 2;
    y = (SCREENHEIGHT - bmp.ih.biHeight) / 2;

    // LCDに画像を表示します。
    draw_bmp(pfb, x, y, bmp.ih.biWidth, bmp.ih.biHeight, bmpdata);

    // フレームバッファのために確保した領域を開放します。
    munmap(pfb, SCREENSIZE);
    // フレームバッファをクローズします。
    close(fd_fb);
    // 画像表示に必要なメモリ領域を開放します。
    free(bmpdata);

    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2016/11/29 20:58

    こちらの質問が他のユーザから「やってほしいことだけを記載した丸投げの質問」という指摘を受けました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

  • ikedas

    2016/11/29 23:42

    提示されたプログラムはどなたか他の方の書いたものでしょうか。そうであれば、プログラムの出所 (書籍なら著者名・書名・ページ数、ウェブならURL) を教えて下さい。

    キャンセル

回答 2

checkベストアンサー

0

Q. 「ここに記載されたコード」で、どのような結果になりましたか? 一部表示ですか? それともスキマだらけ?

…ってのを書いてくださると答えやすいです。ここだけ見ても、どのようなコードを試して、どのような結果になったか分からないです。

まあ、それだけでは身も蓋もないので、ヒントを少々。Linuxのデバイス操作は詳しくないですが、要するに、2重forループの中で、画素の取得、描画をしていますね? ここをいじればいいのです。

まず、c, x, yなんて分かりにくい変数はやめます。i = x-x0, j = y-y0とします。
ループ条件は、i=0; i<w; i++j=h-1; j>=0; j--です。

b = bmpdata[i*3 + j*(w*3+padding) + 0];
g = bmpdata[i*3 + j*(w*3+padding) + 1];
r = bmpdata[i*3 + j*(w*3+padding) + 2];
pfb[(j + y0) * SCREENWIDTH + (i + x0)] = RGB888(r, g, b);

です。これで好きな画素にアクセスできるようになりました。
次に、描画を工夫します。例えば1ピクセルおきに描画するのであれば、

pfb[(j / 2 + y0) * SCREENWIDTH + (i / 2 + x0)] = RGB888(r, g, b);

としたうえで、ループ変数の変化をi++やj--からi+=2やj-=2に変えてあげればピクセルを「飛ばす」ことができます。

一応、参考になるサイトを書いておきます。
http://ishidate.my.coocan.jp/vcpp10_g6/vcpp10_g6.htm
以前はもっとC++に密着した、わかりやすく勉強になるページだったのですが…時代の流れですかね。まあ、10年経った今でも、エッセンスは残っているので読めばヒントになると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/01 16:20

    ありがとうございます。
    とても助かりました。
    画像が上下反転してしまいますが、縮小されて表示できました。

    void draw_bmp(unsigned int *pfb, int x0, int y0, int w, int h, unsigned char *bmpdata)
    {
    int i = i - x0;
    int j = j - y0;
    int padding = (w * 3) % 4;
    unsigned char r, g, b;

    // 開始位置から画像を表示します。
    // 最も下のラインから始まり、
    // 最も上のラインに向かって1ラインずつ画像データを格納します。
    for(j = h - 1; j >= 0; j -= 2){
    for(i = 0; i < w; i += 2){
    // 1pixelから、R,G,B各色のカラーデータを取得します。
    b = bmpdata[i * 3 + j * (w * 3 + padding) + 0];
    g = bmpdata[i * 3 + j * (w * 3 + padding) + 1];
    r = bmpdata[i * 3 + j * (w * 3 + padding) + 2];
    // LCDにカラーデータを表示します。
    pfb[(j / 2 + y0) * SCREENWIDTH + (i / 2 + x0)] = RGB888(r, g, b);
    }
    }
    }

    たぶん、下のラインから取得した画素が描画する時に上のラインから描画されているから上下反転しているのではないかと思いました。

    どこがおかしいのでしょうか。

    キャンセル

0

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • C

    4534questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。