블로그 검색 상위에 jpeg 저장방법이 오랫동안 유지되는 걸로 봐서 이런것도 같이 올려본다. 그리고 jpeg로 저장할 때는 libjpeg 또는 libjpeg-dev 와 같은 라이브러리를 설치하여야 빌드가 가능하다. 독자분들 모두다 잘 쓰고 계시는지 문의 댓글이 없어서 업데이트 하진 않았는데..

     

      그러면 라이브러리를 사용하지않고 리눅스 또는 윈도우에서 바로 읽을 수 있는 이미지를 만들수 있을까? 물론 있다. 리눅스에서 이러한 이미지 포맷을 PPM파일로 저장해주면 xwindow에서 바로 출력이된다. 하지만 마이크로 소프트사의 윈도우에서는 당연히 출력이 되지않을테니, 리눅스와 윈도우에서 모두 읽을 수 있도록 메모리 버퍼의 이미지를 BMP로 저장해주면 된다. 

     

      이를 위해서는 우선 BMP라는 이미지 포맷을 알아야 한다. BMP는 파일 앞의 헤더 뒤로 지정된 데이터에 해당하는 RGB의 값이 저장되는 이미지 포멧이다. 물론 BMP의 옵션을 변경하여 8비트로 처리하면 256컬러, 16비트로 처리하면 65536컬러의 이미지를 저장할 수도 있다. 필자는 24비트를 기준으로 설명하고자 한다.  아니, 필자가 2008년도에 embeddedclub.net에 작성했던 게시물 2개를 취합해서 올려놓는다. 

     

    1. BMP 파일 포멧

      BMP는 Microsoft가 이미지 출력을 위해 고안한 이미지파일 포맷이다. 따라서 정상적인 데이터의 BMP 파일은 윈도우에서 미리보기 또는 그림판에서 확인 및 수정이 가능하다.  자세한 것은 wikipedia.org를 참고한다. 

     

    1.1. 파일 포맷의 정의

      파일 포맷이란 특정한 파일의 구조를 가진 형식을 의미한다. 파일이 어떤방식으로 저장되어있는지를 정의하고있다. 일반적으로 오디오 파일인 wav와 midi 파일도 관련 프로그램에서 인식할 수 있는 정해진 형태로 파일이 저장되어있다는 의미이다. 본 내용에서 bmp 파일 포멧은 저장된 파일이 bmp의 정보로 처리될 수있도록 미리 정해진 구조로 이미지의 정보를 제공하는 영역을 의미한다. 대부분의 파일포맷을 가진 파일은 헤더와 데이터로 구분된다. 파일의 속성정보가 헤더 영역에 위치하고 그 다음 데이터가 압축 또는 raw데이터(비압축의 연속적인 데이터 형식)등으로 배치된다.  

     

    1.2. BMP 헤더

      BMP헤더는 BITMAPFILEHEADER와 BITMAPINFOHEADER로 구성된다. FILEHEADER는 파일 정보를 나타내는 헤더이고 INFO헤더는 이미지 파일의 정보를 나타낸다. 

     

    1) BITMAP FILE HEADER

      비트맵 파일헤더는 다음의 그림과 같은 형태로 구성된다. 

    BITMAP FILE HEADER

        - bfType : 비트맵 타입을 나타냄 'BM'
        - nfSize : 해당 이미지 파일의 크기
        - bfReserved1 : 예약된 공간
        - bfReserved2 : 예약된 공간
        - bfOffBit : 실제 픽셀 정보공간 까지의 거리

     

     

    2) BITMAP INFO HEADER

      BITMAP INFO HEADER는 비트맵 이미지의 속성을 나타내는 헤더이다. 구성은 아래의 그림과 같다. 

     

     

        - biSize : 이 구조체를 저장하기 위한 바이트수 (Default : 40Byte, 확장형 DIB 사용시 변동)
        - biWidth : 비트맵의 가로 크기 (픽셀단위)
        - biHeight : 비트맵의 세로 크기 (픽셀단위)
        - biPlane : 디스플레이시 필요한 플레인 수 (Always : 1)
        - biBitCount : 한 픽셀을 표현하기 위한 비트수
        - biCompression :압축유형 (Default : BI_RGB - 비압축)
        - biSizeImage : 필셀데이터를 저장하기 위한 공간
        - biXPelsPerMeter : 가로 해상도
        - biYPelsPerMeter : 세로 해상도
        - biCirUsed : 실제 사용되는 생상 수 (Default 0 : biBitCount에서 지정한 모든색상 사용)
        - biClrImportant : 화면에 표현하기 위한 색상의 인덱스 (Default 0 : 모든색상)

     

      이야 14년전 게시물인데.. 뭐 아직까지 bmp 파일도 많이 쓰이고 있으니.. 뭐... 어쨋든, 파일에서 오른쪽 클릭해서 속성정보를 보면 이미지 파일의 구성정보가 나오는데, 이는 운영체제에서 이러한 헤더를 읽어서 사용자에게 보여줄 수 있도록 구성되었기에 가능한 것이다. 

     

     2. BMP 파일 정보출력 예제

      음.. BMP 파일은 리눅스에서 저장할 때, 별도의 라이브러리를 사용하지 않았다.

    2.1. bmp_header.h

      bmp 헤더 파일은 다음과 같이 구현했었다. (과거형...) 아마 임베디드 보드 LCD에 BMP 파을을 출력하기전에 정보를 출력하는 예제 목적으로 만든것을 추측된다. 

     

    /* Bmp File display to Frame-buffer example
     * FILE : bmp_header.h */

    #ifndef _BMPINFO_ 
    #define _BMPINFO_

    // make type definition - windows type
    typedef unsigned short U16;
    typedef unsigned short WORD;
    typedef unsigned int DWORD;
    typedef unsigned int LONG;
    typedef unsigned char BYTE;

    // make type definition - bmp file header 
    typedef struct tagBITMAPFILEHEADER {
        WORD bfType;
        DWORD bfSize;
        WORD bfReserved1;
        WORD bfReserved2;
        DWORD bfOffBits;
    } __attribute((packed)) BITMAPFILEHEADER;

    // make type definition - bmp image infomation header
    typedef struct tagBITMAPINFOHEADER {
        DWORD biSize;
        LONG biWidth;
        LONG biHeight;           
        WORD    biplanes;           
        WORD    biBitCount;         
        DWORD   biCompression;     
        DWORD   biSizeImage;        
        LONG    biXPelsPerMeter;     
        LONG    biYPelsPerMeter;    
        DWORD   biClrUsed;          
        DWORD   biClrImportant;      
    } __attribute((packed)) BITMAPINFOHEADER;

    /* other mathod : Group Attribute Packed Mathod
       #pragma pack(1) or pragma pack() */

    #endif

     

     2.2. bmp_info.c

      저장된 bmp 파일의 정보를 소스코드는 아래와 같다. 

     

    /* Bmp File display to Frame-buffer example
     * FILE : bmp_info.c */

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <sys/types.h>
    #include <linux/fb.h>

    #include "bmp_header.h"

    int main(int argc, char **argv) 
    {
        BITMAPFILEHEADER BmpFileHd;
        BITMAPINFOHEADER BmpInfoHd;  

        size_t retval;

        // FrameBuffer
        int fb_fd;
        struct fb_var_screeninfo fvs;

        FILE *read_fp=NULL;

        // Argument Check
        if(argc !=2){
            printf("Usage : %s [FileName]\n",argv[0]);
           return -1;
        }
     
        // File Open
        if ((read_fp = fopen(argv[1],"rb")) == NULL) {
            printf("File Open Error\n");
            exit(1);
        } 

        // File Read - Bmp File Header
        fread(&BmpFileHd, sizeof(BITMAPFILEHEADER), 1, read_fp); 
        if(BmpFileHd.bfType!=0x4D42) {
            printf("Not Bitmap file or Unsupport Bitmap file\n");
            exit(1);
        }

        printf("* BMP File Header - \n");
        printf("  Type    : 0x%04X\n", BmpFileHd.bfType);    
        printf("  Size    : %d\n", BmpFileHd.bfSize);   
        printf("  Offbits : %d\n", BmpFileHd.bfOffBits);  

        // File Read - Bmp Image infomation header
        fread(&BmpInfoHd, sizeof(BITMAPINFOHEADER), 1, read_fp);
        printf("* BMP Info Header - \n");
        printf("  FIleName   : %s\n",argv[1]); 
        printf("  Infosize   : %d\n", BmpInfoHd.biSize);      
        printf("  Width      : %d\n", BmpInfoHd.biWidth);      
        printf("  Height     : %d\n", BmpInfoHd.biHeight);
        printf("  Planes     : %d\n", BmpInfoHd.biplanes);
        printf("  Bitcount   : %d\n", BmpInfoHd.biBitCount); 
        printf("  Compressin : %d\n", BmpInfoHd.biCompression); 
        printf("  Sizeimage  : %d\n", BmpInfoHd.biSizeImage);   
        printf("  XPelsPerMeter : %d\n", BmpInfoHd.biXPelsPerMeter); 
        printf("  YPelsPerMeter : %d\n", BmpInfoHd.biYPelsPerMeter);
        printf("  ClrUsed       : %d\n", BmpInfoHd.biClrUsed); 
        printf("  ClrImportant  : %d\n", BmpInfoHd.biClrImportant); 

        fclose(read_fp);

        return 0;
    }

     

      위의 프로그램을 gcc로 컴파일 하면, 저장된 bmp 파일의 정보를 출력할 수 있다. 조금 눈치가 빠른 독자라면 그러면 헤더는 출력했으니, 실제 데이터는 어디에 있을까? 파일헤더의 bfOffBit이실제 픽셀 정보공간 까지의 거리 즉, 데이터는 파일 시작 위치에서 + bfOffBit에 위치한 바이트부터 시작한다.

     

      24비트(R,G,B) 이미지라면 해당 위치부터 B,G,R,B,G,R.... 순으로 1바이트가 하나의 색요소를 가진다.  이렇게 이야기하니까 조금 어렵게 느껴지는데 8비트(1바이트)로 구성된 R,G,B 3바이트가 LCD에 출력되는 하나의 점을 구성하는 데이터가 된다. 따라서 8*3 = 24(1픽셀 = 점 하나), 그래서 24비트 컬러또는 RGB888 이라는 색상구조를 가지고 있다고 이야기 한다. 물론, 색상구조에는 RGB32(RGB24에서 Alpha값을 가진형태), RGB24(RGB888), RGB16(RGB565)나 YUV등 다양한 형태가 있다는 것도 알아두면 좋다. 

     

     출력 예제를 보니까, 예전에 대부분 24비트 이미지를 가져와서 16비트 컬러 LCD에 출력하는 예제밖에 없는것 같다. 흠.. PC에서 테스트하기 위한 목적의 예제도 만들었던것 같은데.. 안보인다. 

     

      BMP는 시간이 되는데로 예제를 작성해서 추가로 업데이트 하도록 하겠다. QT에서는 그냥 불러와서 출력하면되는데, C로 되어있으므로 임베디드 장치에 연결된 LCD에 직접 출력하지 않으려면 다소 불편하다. 텍스트 기반에서는 SDL(Simple Direct media Library)을 이용해서 출력하기도 했는데, 요즘세상에 굳이 SDL을 쓰기에도 그렇고.. 야튼 이부분은 조금 고민해보고 진행해야겠다. 

     

     

    20231222 수정 : RGB, RGB순 --> BGRBGR순.

    반응형
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기