Busan IT/영상처리2015. 11. 9. 13:47

==================================Outline====================================

BMP파일 가로 크기 패딩 처리

컬러 BMP파일 회색 변환

히스토그램을 사용한 평활화

명암 증가

----------------------------------------------------------------------------

BMP파일 가로 크기 패딩 처리

 

BMP파일의 가로 크기는 4의 배수여야 하는 규칙을 가지고 있다. 가로의 크기가 4의 배수가 아닐 경우에 사용자가 임의적으로 파일을 쓰게 되면 패딩에 값이 들어가기 때문에 BMP파일의 이미지가 깨지게 된다.

 

RGB BMP파일을 가로 픽셀의 크기가 401, 세로 픽셀의 크기가 400일로 만들 경우 파일의 용량은 헤더파일 54byte까지 추가하여 481,254byte이다.

 

 

하지만 패딩이 발생하여 이미지의 크기는 481,654byte가 된다. 올바른 자리에 데이터를 삽입하기 위하여 패딩 값을 고려해주어야 한다.

패딩은 가로 픽셀의 크기를 4의 배수로 맞춰주기 위한 보수 값이다.

BMP파일의가로의 크기는 4의 배수일 때만 정상적으로 수정이 가능하기 때문에 패드 값을 처리해주는 코드를 삽입 한다.

 

가로의 값이 401일 경우를 생각해보자. 각 픽셀당 RGB3byte를 차지함으로 한 줄의 크기는 1203이되고 이 때 4의 배수를 위한 패딩의 값은 1이다.

 

402일 경우에는 패딩 값이 2, 403일 경우는 패딩 값이 3이다. , 4로 나눈 나머지의 값이 패딩이 된다.

 

uiPad = stBIHead.biWidth % 4; //패딩 값


패딩 값이 설정되고 나면 이 값을 데이터의 값을 입력하는 이중 for문에 삽입하여 패딩 값을 처리해 주어야한다. 패딩 값은 라인 수 * 패딩 값이 되는 것을 유의하여야 한다.

for(uiCntY=0; uiCntY < stBIHead.biHeight; ++uiCntY)
{
  for(uiCntX=0; uiCntX < stBIHead.biWidth; ++uiCntX)
  {  
    //평균값 연산
    uiAVG = ( ucpORG[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 0 + (uiCntY*uiPad)] +
      ucpORG[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 1 + (uiCntY*uiPad)] +
      ucpORG[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 2 + (uiCntY*uiPad)] )/3;



      ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 0 + (uiCntY*uiPad)] = uiAVG; 

//blue  
    ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 1 + (uiCntY*uiPad)] = uiAVG; 

//green
    ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 2 + (uiCntY*uiPad)] = uiAVG; 

//red                        

  }
}

패딩 설정을 해주고나면 가로 픽셀이 4의 배수가 아니더라도 데이터가 원하는대로 입력된다.

 

 

컬러 BMP파일 회색 변환


컬러 BMP사진을 흑백으로 변환해보자. 컬러를 흑백으로 변환하는 건 쉽다. 원래의 이미지를 저장한 다음 RGB의 각 값만을 추출하여 표시해보자.

 

동적할당을 받아 원래의 이미지를 저장한다. 이 때 메모리를 복사하는 ‘memcopy'함수를 사용한다.

 

 

원하는 색만 추출하기 위해서는 다른 값은 ‘0’을 삽입하고 원하는 색은 주석 처리해준다.


원하는 값이 나오는 것을 확인할 수 있다.

 

 

 

디지털 영상의 히스토그램

 

RGB의 값의 정도를 나타내주는 그래프이다. 히스토그램을 활용하여 평활화를 시켜보자. 히스토그램의 평활화는 비슷한 값의 집단을 하나로 합쳐준다.

 

평활화의 단계는 다음과 같다.

 

1단계: 각 픽셀의 명도수를 구한다.

 

 

2단계: 명도의 단계별로 누적합을 구하는 작업이다.

 

3단계: 누적된 값을 픽셀의 개수로 나눈 후 명도의 단계 - 1의 값으로 곱해준다.

 

 

요약하면 아래와 같다.




히스토그램을 사용한 평활화 

 

히스토그램을 위한 코딩을 해보자.

 

1. 히스토그램의 생성

//// Equialization
//// 1단계: 히스토그램 생성
for(uiCntY=0; uiCntY < stBIHead.biHeight; ++uiCntY)
{
  for(uiCntX=0; uiCntX < stBIHead.biWidth; ++uiCntX)
  {    
    ++uiCntB[ ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 0 + (uiCntY*uiPad)] ]; //blue      
    ++uiCntG[ ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 1 + (uiCntY*uiPad)] ]; //green      
    ++uiCntR[ ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 2 + (uiCntY*uiPad)] ]; //red  
  }
} 

각 픽셀 값의 RGB 값을 추출하여 각 값이 몇 개나 있는지 빈도수를 계산해준다.


2. 누적합 계산 

정규화를 연산을 위해 누적합을 계산해준다.

//// 2단계: 누적합 계산
uiSumR[0= uiCntR[0];
uiSumG[0= uiCntG[0];
uiSumB[0= uiCntB[0];

for(uiCntX = 1256 > uiCntX; ++uiCntX)
{
  uiSumR[uiCntX] = uiSumR[uiCntX-1] + uiCntR[uiCntX];
  uiSumG[uiCntX] = uiSumG[uiCntX-1] + uiCntG[uiCntX];
  uiSumB[uiCntX] = uiSumB[uiCntX-1] + uiCntB[uiCntX];
      
}

3. 정규화 

//// 3단계: 정규화
uiCntY = stBIHead.biWidth * stBIHead.biHeight;
for(uiCntX = 0256 > uiCntX; ++uiCntX)
{
  uiCntR[uiCntX] = (uiSumR[uiCntX]*255)/uiCntY;
  uiCntG[uiCntX] = (uiSumG[uiCntX]*255)/uiCntY;
  uiCntB[uiCntX] = (uiSumB[uiCntX]*255)/uiCntY;        

} 

정규화 계산식을 이용하여 정규화해준다.

 

4. 이미지에 적용

/// 이미지에 적용
for(uiCntY=0; uiCntY < stBIHead.biHeight; ++uiCntY)
{
  for(uiCntX=0; uiCntX < stBIHead.biWidth; ++uiCntX)
  {    
    ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 0 + (uiCntY*uiPad)] 
    = uiCntB[ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 0 + (uiCntY*uiPad)]]; //blue
    
    ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 1 + (uiCntY*uiPad)]
    = uiCntG[ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 1 + (uiCntY*uiPad)]]; //green      
      ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 2 + (uiCntY*uiPad)]
    = uiCntR[ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 2 + (uiCntY*uiPad)]]; //red  
  }

} 

정규화된 값을 이미지에 적용하여 평활화시킨다. 

 

 

 

 

 

 

명암 증가

 

RBG값에 50을 더하여 값을 삽입해보자. 값이 255를 넘을 경우 최대 값이 255가 되도록 설정한다.

//jpg파일은 압축기법을 공부하지 않고서는 BMP파일처럼 조작하기 어렵다.







반응형

'Busan IT > 영상처리' 카테고리의 다른 글

비트맵 파일 규격 규칙  (0) 2015.11.06
비트맵 구조체  (0) 2015.11.05
Posted by newind2000
Busan IT/영상처리2015. 11. 6. 16:50

==================================Outline====================================

비트맵 파일 규격 규칙

----------------------------------------------------------------------------

 비트맵 파일 규격 규칙

 

세로 줄과 가로 줄을 그어보자. 

비트맵 파일은 좌우는 유지되고 상하를 뒤집어서 저장하는 것을 알 수 있다.


 

영상처리에서 X, Y축 대칭 기술은 흔히 쓰인다.

 

비트맵 크기를 조절할 때 가로 축의 크기가 4의 배수가 아닐 경우에 데이터를 쓰게 되면 오류가 발생한다.



 

대신 가로의 값이 4의 배수일 경우 오류가 발생하지 않는다.

 

가로 1픽셀식 늘려서 값을 크기를 계산기로 계산해보고 실제 파일의 크기를 알아보자.

가로 축은 항상 4의 배수의 용량을 가져야 한다는 규칙을 가지고 있다. 가로 픽셀의 용량이 4의 배수가 아닐 경우 4의 배수로 채우기 위한 byte를 더해준다.

반응형
Posted by newind2000
Busan IT/영상처리2015. 11. 5. 22:51

==================================Outline====================================

비트맵 구조체

----------------------------------------------------------------------------

비트맵 구조체

크기가 가로 400픽셀, 세로 400픽셀인 비트맵 파일을 임의로 생성하여 비트맵파일에 대해서 알아보기로 하자.

 

 


각 픽셀이 RGB로 표현되기 때문에 이 비트맵 파일의 용량은 (400 * 400) * 3이고, 54byte는 헤더파일이다헤더 파일은 실제내용을 간추린 정보를 제공하는데 이것을 오버 헤더라고 한다.


 

비트맵 파일은 픽셀 단위로 데이터를 저장한다. 1byte(8bits)에는 한가지 색에 대한 정보를 가지고 있다.  1픽셀은 3byte로 구성되는데,  각 바이트는 빨(Red), 녹(Green), 파(Blue)로 이루어져 있다.

 

**빛의 3원색:  빨강, 녹색, 파랑 

**색의 3원색:  자주, 노랑, 청록

 

 

** 빛은 섞을수록 밝아지고 색은 섞을수록 어두워진다.

위에서 만든 비트맵 파일을 헥사뷰를 사용하여 출력해본다. 54byte가 'FF'가 아닌 값으로 표시되고 있음을 알 수 있다. 

 

54Byte는 비트맵 파일의 헤더부분이며 비트맵 파일에 대한 정보를 담고 있다. 비트맵 파일의 구조체를 살펴보도록 하자. 


 

/* BITMAPFILEHEADER 구조체 */

typedef struct tagBITMAPFILEHEADER{ //bmfh

WORD bfType; // 파일의 형태, 0x42, 0x4d (BM) 이어야함

WORD bfSize; // 비트맵 파일의 크기 (Byte단위)

WORD bfReserved1; // 예약. 0으로 설정

WORD bfReserved2; // 예약2. 0으로설정

DWORD bfOffBits; // 실제 비트맵데이터까지의 오프셋값

// 실제로는 bfOffBits = BITMAPFILEHEADER크기 + BITMAPINFOHEADER크기 + RGBQUAD 구조체배열의크기 이다.

} BITMAPFILEHEADER;

 

/* BITMAPINFOHEADER 구조체 */

typedef struct tagBITMAPINFOHEADER{ //bmfh

DWORD biSize; // 이 구조체의 크기. 구조체 버전확인할수 있다.

LONG biWidth; // 비트맵의 가로 픽셀수

LONG biHeight; // 비트맵의 세로 픽셀수

WORD biPlanes; // 플레인의 갯수 반드시 1이어야함

WORD biBitCount; // 한 픽셀이 구성되는 비트의수

DWORD biCompression; // 압축방법. BI_RGB일땐 비압축 BI_RLE8, BI_RLE4인경우 run length encode방법으로 압축

DWORD biSizeImage; // 이미지의 크기. 압축이 안되어있을때는 0

LONG biXPelsPerMeter; // 가로 해상도

LONG biYPelsPerMeter; // 세로 해상도

DWORD biClrUsed; // 색상테이블을 사용하였을때 실제 사용되는 색상수

DWORD biClrImportant; // 비트맵을 출력하는데 필수 색상수

} BITMAPINFOHEADER;

 

비트맵 파일 구조체를 참고하여 구조체 멤버들을 출력해보자. 

 

400 * 400 크기의 BMP파일의 경우 한 픽셀당 3byte를 차지하고 그 값을 red, greeb, blue의 정도는 8bit(256) 나누어 표시할 수 있다. 파일의 총 크기는 480054byte이고 54byte는 헤더파일임으로 파일의 시작점에서 54byte 떨어진 지점부터의 데이터를 조작함으로써 BMP파일을 변형시킬 수 있다.

BMP파일을 조작하여 여러 가지 색깔로 변형시켜보자.

**BMP이미지를 수정하여 원본에 덮어 쓰도록 하자.

각 색에 대한 출력 정보는 0 ~ FF로 표현된다. '0'이 가장 낮은 단계이며 'FF'가 가장 높은 단계이다. 가로 픽셀이 400, 세로 픽셀이 400인 비트맵 구조체에서 가로 한 줄의 용량은 400 * 3이다. 좌측 상단에서 우측 하단까지 'for문'을 사용하여 데이터의 값을 입력할 값을 설정해주면 된다.

각 픽셀의 첫 번째 바이트는 blue, 두 번째 바이트는 green, 세 번째 바이트는 red이다. 비트맵 파일의 정보를 수정하여 색이 어떻게 바뀌는지 출력해보자.







소스 코드는 아래와 같다.

/*** 소스 ***/ 

#include <stdio.h>
#include <fcntl.h>
#include <windows.h>


int main(int iNum, char * cpArr[])
{
  int iRtn;
  int iFd;
  BITMAPFILEHEADER stBFHead;
  BITMAPINFOHEADER stBIHead;
  char cBuf[16*16];
  unsigned char * ucpBuf;
  
  unsigned int uiCntX;
  unsigned int uiCntY;

  

  iFd = open(cpArr[1], O_RDWR|O_BINARY);  
  if(iFd == 0)
  {
    printf("파일을 열 수 없습니다.\n");
    return -1;
  }

  iRtn = read(iFd, &stBFHead, sizeof(BITMAPFILEHEADER));
  if(iRtn == 0)
  {
    printf("파일을 읽을 수 없습니다.\n");
    close(iFd);            
    return -1;
  }
  if0x4D42 != (stBFHead.bfType) )
  {
    printf("BMP파일이 아닙니다.\n");
    close(iFd);            
    return -1;
  }
  iRtn = lseek(iFd, sizeof(BITMAPFILEHEADER), SEEK_SET);
  if(iRtn != sizeof(BITMAPFILEHEADER) )
  {
    printf("읽어들일 부분이 잘못 지정되었습니다.\n");
    close(iFd);            
    return -1;
  }
  iRtn = read(iFd, &stBIHead, sizeof(BITMAPINFOHEADER));
  if(iRtn == 0)
  {
    printf("INFO헤더가 존재하지 않습니다.\n");
    close(iFd);            
    return -1;
  }

  ucpBuf = (void *)malloc(stBIHead.biSizeImage);
  if(0 == ucpBuf)
  {
    printf("사용 가능한 메모리가 부족합니다.\n");
    close(iFd);
    return -1;
  }
  lseek(iFd, sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER), SEEK_SET);
  iRtn = read(iFd, ucpBuf, stBIHead.biSizeImage);
  

  if(iRtn == 0)
  {
    printf("데이터를 읽을 수 없습니다.\n");
    close(iFd);        
    free(ucpBuf);
    return -1;
  }
  for(uiCntY=0; uiCntY < stBIHead.biHeight; ++uiCntY)
  {
    for(uiCntX=0; uiCntX < stBIHead.biWidth; ++uiCntX)
    {
      ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 0= 0//blue  
      ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 1= 0//green
      ucpBuf[uiCntY * (stBIHead.biWidth * 3) + (uiCntX * 3) + 2= 0xFF; //red            
    }
  }
  lseek(iFd, 54, SEEK_SET);
  iRtn = write(iFd, ucpBuf, stBIHead.biSizeImage);
  if(iRtn == 0)
  {
    printf("데이터 쓰기에 실패 했습니다.\n");
    close(iFd);        
    free(ucpBuf);
    return -1;
  }

  lseek(iFd, 54, SEEK_SET);
  iRtn = read(iFd, cBuf, 16*16);  

  
  
  
  close(iFd);
  free(ucpBuf);
  
  return 0;

} 

 

 

 

 

반응형
Posted by newind2000