해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.



※ 다중 문서 응용 프로그램(MDI)에서 화면 출력

(github => https://github.com/fbf7290/ImageProcessing/tree/MDIViewer)

 

 

//위 그림에서 보이는 화면 출력 메뉴에 대한 이벤트 처리기
void CMDIViewerDoc::OnMenuView()
{
	// TODO: 여기에 명령 처리기 코드를 추가합니다.
	m_image = LoadImageFromDialog(); //탐색기를 선택한 비트맵 파일 반환
	if (!m_image.IsEmpty())          //영상이 제대로 읽어졌는 지 판단
	{
       //View 클래스에 화면을 갱신하라고 메시지를 보냄 => OnDraw()함수가 호출됨
		UpdateAllViews(NULL);       
	}
}

- LoadImageFromDialog() 함수

: CFileDialog를 통해 탐색기 안에 있는 비트맵 파일을 불러오는 기능을 한다.

#include "../stdafx.h"
#include "LoadImageFileDialog.h"
CByteImage LoadImageFromDialog()
{
	CByteImage image;	// 반환할 영상
	// 파일 대화상자에서 BMP 형식의 파일만 보이도록 하는 필터의 정의
	TCHAR szFilter[] = _T("BMP File (*.BMP) | *.BMP; | All Files(*.*)|*.*||");

	// 입력 영상 읽기
	CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter);
	if (IDOK != dlg.DoModal()) // 대화 상자 열기
	{
		return image;
	}

	char * strPathName = _strdup(CT2A(dlg.GetPathName())); // 영상 파일 경로 획득
	image.LoadImage(strPathName); // 영상 읽기

	return image;
}

 

 

- onDraw()

: 첫 번째 코드에서 메뉴 클릭 이벤트 함수 내에 UpdateAllViews(NULL); 함수가 호출되면 위 함수가 호출된다. onDraw()함수는 View 클래스 화면을 갱신하는 역할을 한다.

void CMDIViewerView::OnDraw(CDC* pDC)
{
	CMDIViewerDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 여기에 원시 데이터에 대한 그리기 코드를 추가합니다.
	CByteImage& m_image = pDoc->m_image;
	if (m_image.IsEmpty()) return;

   //화면에 영상을 출력하기 위한 함수
	_DrawImage(pDC->m_hDC, m_image,
		0, 0, m_image.GetWidth(), m_image.GetHeight(),
		0, 0, m_image.GetWidth(), m_image.GetHeight());
}

-_DrawImage() 함수

: image 객체 비트밉의 (nSrcX, nSrcY)좌표를 시작으로 너비 nSrcW, 높이 nSrcH 만큼의 비트맵을 메인 프로그램의 (nDstX, nDstY) 좌표에 너비 nDstW, 높이 nDstH 크기로 출력하는 함수이다. 이 함수 안에는 StretchDIBits라는 MFC에서 제공하는 함수를 사용한다. 먼저 StretchDIBits 함수를 알아보자.

StretchDIBits() 함수는 비트맵 형식 영상 정보 헤더와 함게 전달된 영상을 디바이스 컨텍스트를 이용하여 화면에 출력하는 함수로 wingdi.h에 선언되어 있다. 입력 영상에서 출력을 원하는 영역을 설정할 수 있고, 이 영역이 화면에서 출력되는 영역도 설정할 수 있다. 화면이 성공적으로 출력되면 출력된 영상의 행 수를 반환하고 출력이 정상적으로 이루어 지지 않았다면 0을 반환한다. 이 함수가 실제로 수행하는 것은 매개변수로 받은 비트맵 형식의 입력 영상을 디바이스 컨텍스트의 메모리로 복사하는 작업이다.

 

- 위 함수 매개변수인 BITMAPINFO 구조체는 비트맵 파일의 영상 정보 헤더인 BITMAPINFOHEADER 구조체와 팔레트 정보를 담은 RGBQUAD 구조체의 포인터를 모아 놓은 것이다.

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} BITMAPINFO

- 매개변수 iUsage : BITMAPINFO 구조체를 통하여 넘겨진 팔레트의 사용 방법을 결정한다. 보통 팔레트에 저장한 값을 실제 RGB 색상 값으로 사용하는 방법인 DIB_RGB_COLORS를 사용한다.

 

- 매개 변수 rop : 입력 영상의 픽셀 값으로부터 출력 화면의 픽셀 값을 구하는 방법을 결정하는 것으로 그대로 복사하는 방법(SRCCOPY), 반전하여 복사하는 방법(NOTCOPY), 현재 화면의 픽셀 값과 OR 연산을 하는 방법(SRCPAINT), 현재 화면의 픽셀 값과 AND 연산을 하는 방법(SRCAND) 등이 있다.

 

 

 

bool _DrawImage(HDC hdc, const CByteImage& image,
	int nSrcX, int nSrcY, int nSrcW, int nSrcH,
	int nDstX, int nDstY, int nDstW, int nDstH)
{
	if (image.IsEmpty()) return false; // 빈 영상이면 바로 종료

	int nWidth = image.GetWStep() / image.GetChannel();
	int nHeight = image.GetHeight();
	int nBPP = image.GetChannel() * 8;

	// 256색 회색조 또는 트루 컬러 영상만 출력 가능
	ASSERT(nWidth > 0 && nHeight > 0 && (nBPP == 8 || nBPP == 24));

	BITMAPINFOHEADER *bmih = &(((BITMAPINFO*)bmiBuf)->bmiHeader); //bmiBuf는 밑에 설명
	memset(bmih, 0, sizeof(*bmih));
	bmih->biSize = sizeof(BITMAPINFOHEADER);
	bmih->biWidth = nWidth;
	bmih->biHeight = -nHeight;	// 실제 비트맵 파일이 저장될 때는 상하가 반대로 저장 되므로 위에서 아래로 출력. 
	bmih->biPlanes = 1;         //무조건 1이어야함
	bmih->biBitCount = nBPP;
	bmih->biCompression = BI_RGB; // 압축 사용 안함

	if (nBPP == 8)   //회색조 영상일 경우 팔레트 저장. GrayPalette는 밑에서 설명
		memcpy(((BITMAPINFO*)bmiBuf)->bmiColors, GrayPalette, 256 * sizeof(RGBQUAD));

	SetStretchBltMode(hdc, COLORONCOLOR); //StretchDIBits로 비트맵을 축소 시 화면이 깨지는 거 방지
	::StretchDIBits(
		hdc,
		nDstX, nDstY, nDstW, nDstH,
		nSrcX, nSrcY, nSrcW, nSrcH,
		image.GetPtr(),
		(LPBITMAPINFO) &(((BITMAPINFO*)bmiBuf)->bmiHeader),
		DIB_RGB_COLORS,
		SRCCOPY);

	return true;
}

 

 

static BYTE bmiBuf[sizeof(BITMAPINFOHEADER) + 1024];
static const RGBQUAD GrayPalette[256] =
{
	{ 0,   0,   0,  0 },{ 1,   1,   1,  0 },{ 2,   2,   2,  0 },{ 3,   3,   3,  0 },{ 4,   4,   4,  0 },{ 5,   5,   5,  0 },{ 6,   6,   6,  0 },{ 7,   7,   7,  0 },{ 8,   8,   8,  0 },{ 9,   9,   9,  0 },
	{ 10,  10,  10,  0 },{ 11,  11,  11,  0 },{ 12,  12,  12,  0 },{ 13,  13,  13,  0 },{ 14,  14,  14,  0 },{ 15,  15,  15,  0 },{ 16,  16,  16,  0 },{ 17,  17,  17,  0 },{ 18,  18,  18,  0 },{ 19,  19,  19,  0 },
	{ 20,  20,  20,  0 },{ 21,  21,  21,  0 },{ 22,  22,  22,  0 },{ 23,  23,  23,  0 },{ 24,  24,  24,  0 },{ 25,  25,  25,  0 },{ 26,  26,  26,  0 },{ 27,  27,  27,  0 },{ 28,  28,  28,  0 },{ 29,  29,  29,  0 },
	{ 30,  30,  30,  0 },{ 31,  31,  31,  0 },{ 32,  32,  32,  0 },{ 33,  33,  33,  0 },{ 34,  34,  34,  0 },{ 35,  35,  35,  0 },{ 36,  36,  36,  0 },{ 37,  37,  37,  0 },{ 38,  38,  38,  0 },{ 39,  39,  39,  0 },
	{ 40,  40,  40,  0 },{ 41,  41,  41,  0 },{ 42,  42,  42,  0 },{ 43,  43,  43,  0 },{ 44,  44,  44,  0 },{ 45,  45,  45,  0 },{ 46,  46,  46,  0 },{ 47,  47,  47,  0 },{ 48,  48,  48,  0 },{ 49,  49,  49,  0 },
	{ 50,  50,  50,  0 },{ 51,  51,  51,  0 },{ 52,  52,  52,  0 },{ 53,  53,  53,  0 },{ 54,  54,  54,  0 },{ 55,  55,  55,  0 },{ 56,  56,  56,  0 },{ 57,  57,  57,  0 },{ 58,  58,  58,  0 },{ 59,  59,  59,  0 },
	{ 60,  60,  60,  0 },{ 61,  61,  61,  0 },{ 62,  62,  62,  0 },{ 63,  63,  63,  0 },{ 64,  64,  64,  0 },{ 65,  65,  65,  0 },{ 66,  66,  66,  0 },{ 67,  67,  67,  0 },{ 68,  68,  68,  0 },{ 69,  69,  69,  0 },
	{ 70,  70,  70,  0 },{ 71,  71,  71,  0 },{ 72,  72,  72,  0 },{ 73,  73,  73,  0 },{ 74,  74,  74,  0 },{ 75,  75,  75,  0 },{ 76,  76,  76,  0 },{ 77,  77,  77,  0 },{ 78,  78,  78,  0 },{ 79,  79,  79,  0 },
	{ 80,  80,  80,  0 },{ 81,  81,  81,  0 },{ 82,  82,  82,  0 },{ 83,  83,  83,  0 },{ 84,  84,  84,  0 },{ 85,  85,  85,  0 },{ 86,  86,  86,  0 },{ 87,  87,  87,  0 },{ 88,  88,  88,  0 },{ 89,  89,  89,  0 },
	{ 90,  90,  90,  0 },{ 91,  91,  91,  0 },{ 92,  92,  92,  0 },{ 93,  93,  93,  0 },{ 94,  94,  94,  0 },{ 95,  95,  95,  0 },{ 96,  96,  96,  0 },{ 97,  97,  97,  0 },{ 98,  98,  98,  0 },{ 99,  99,  99,  0 },
	{ 100, 100, 100,  0 },{ 101, 101, 101,  0 },{ 102, 102, 102,  0 },{ 103, 103, 103,  0 },{ 104, 104, 104,  0 },{ 105, 105, 105,  0 },{ 106, 106, 106,  0 },{ 107, 107, 107,  0 },{ 108, 108, 108,  0 },{ 109, 109, 109,  0 },
	{ 110, 110, 110,  0 },{ 111, 111, 111,  0 },{ 112, 112, 112,  0 },{ 113, 113, 113,  0 },{ 114, 114, 114,  0 },{ 115, 115, 115,  0 },{ 116, 116, 116,  0 },{ 117, 117, 117,  0 },{ 118, 118, 118,  0 },{ 119, 119, 119,  0 },
	{ 120, 120, 120,  0 },{ 121, 121, 121,  0 },{ 122, 122, 122,  0 },{ 123, 123, 123,  0 },{ 124, 124, 124,  0 },{ 125, 125, 125,  0 },{ 126, 126, 126,  0 },{ 127, 127, 127,  0 },{ 128, 128, 128,  0 },{ 129, 129, 129,  0 },
	{ 130, 130, 130,  0 },{ 131, 131, 131,  0 },{ 132, 132, 132,  0 },{ 133, 133, 133,  0 },{ 134, 134, 134,  0 },{ 135, 135, 135,  0 },{ 136, 136, 136,  0 },{ 137, 137, 137,  0 },{ 138, 138, 138,  0 },{ 139, 139, 139,  0 },
	{ 140, 140, 140,  0 },{ 141, 141, 141,  0 },{ 142, 142, 142,  0 },{ 143, 143, 143,  0 },{ 144, 144, 144,  0 },{ 145, 145, 145,  0 },{ 146, 146, 146,  0 },{ 147, 147, 147,  0 },{ 148, 148, 148,  0 },{ 149, 149, 149,  0 },
	{ 150, 150, 150,  0 },{ 151, 151, 151,  0 },{ 152, 152, 152,  0 },{ 153, 153, 153,  0 },{ 154, 154, 154,  0 },{ 155, 155, 155,  0 },{ 156, 156, 156,  0 },{ 157, 157, 157,  0 },{ 158, 158, 158,  0 },{ 159, 159, 159,  0 },
	{ 160, 160, 160,  0 },{ 161, 161, 161,  0 },{ 162, 162, 162,  0 },{ 163, 163, 163,  0 },{ 164, 164, 164,  0 },{ 165, 165, 165,  0 },{ 166, 166, 166,  0 },{ 167, 167, 167,  0 },{ 168, 168, 168,  0 },{ 169, 169, 169,  0 },
	{ 170, 170, 170,  0 },{ 171, 171, 171,  0 },{ 172, 172, 172,  0 },{ 173, 173, 173,  0 },{ 174, 174, 174,  0 },{ 175, 175, 175,  0 },{ 176, 176, 176,  0 },{ 177, 177, 177,{ 208, 208, 208,  0 },{ 209, 209, 209,  0 },
	{ 210, 210, 210,  0 },{ 211, 211, 211,  0 },{ 212, 212, 212,  0 },{ 213, 213, 213,  0 },{ 214, 214, 214,  0 },{ 215, 215, 215,  0 },{ 216, 216, 216,  0 },{ 217, 217, 217,  0 },{ 218, 218, 218,  0 },{ 219, 219, 219,  0 },
	{ 220, 220, 220,  0 },{ 221, 221, 221,  0 },{ 222, 222, 222,  0 },{ 223, 223, 223,  0 },{ 224, 224, 224,  0 },{ 225, 225, 225,  0 },{ 226, 226, 226,  0 },{ 227, 227, 227,  0 },{ 228, 228, 228,  0 },{ 229, 229, 229,  0 },
	{ 230, 230, 230,  0 },{ 231, 231, 231,  0 },{ 232, 232, 232,  0 },{ 233, 233, 233,  0 },{ 234, 234, 234,  0 },{ 235, 235, 235,  0 },{ 236, 236, 236,  0 },{ 237, 237, 237,  0 },{ 238, 238, 238,  0 },{ 239, 239, 239,  0 },
	{ 240, 240, 240,  0 },{ 241, 241, 241,  0 },{ 242, 242, 242,  0 },{ 243, 243, 243,  0 },{ 244, 244, 244,  0 },{ 245, 245, 245,  0 },{ 246, 246, 246,  0 },{ 247, 247, 247,  0 },{ 248, 248, 248,  0 },{ 249, 249, 249,  0 },
	{ 250, 250, 250,  0 },{ 251, 251, 251,  0 },{ 252, 252, 252,  0 },{ 253, 253, 253,  0 },{ 254, 254, 254,  0 },{ 255, 255, 255,  0 }
};

bmiBuf의 크기에 1024를 더해준 이유는 팔레트의 최대 크기가 1024byte이기 때문이다. RGBQUAD 구조체는 BYTE형 변수 4개가 모인 것이이고 팔레트의 최대 갯수가 256개이므로 최대 크기가 1024이다(위의 GrayPalette변수가 그렇다). 또한 GrayPalette 변수는 회색조 영상의 팔레트로 사용할 RGBQUAD 구조체의 배열로서 {0,0,0,0}에서 {255,255,255,255}의 정적 변수로 선언해 두고 사용한다.

해당 포스트는 "자바 객체지향 디자인 패턴", "JAVA 언어로 배우는 디자인 패턴 입문" 책의 내용을 요약한 것이다.



※ 데코레이터(Decorator) 패턴

: 기존에 구현되어 있는 클래스에 추가할 수 있는 기능의 종류가 많을 경우 코드상에서 동적으로 기능을 추가하기 위한 패턴으로 데코레이터를 통해 클래스의 기능이 확장된다.

데코레이터가 적용된 대표적인 예로 자바 I/O가 있다.

Reader reader = new BufferedReader(new FileReader("파일명"));

FileReader 클래스가 BufferedReader 데코레이터 클래스를 통해 기능이 확장된 예이다.

 

 

ex)도로를 간단한 선으로 표시하는 기본적인 기능을 가진 네비게이션 SW로, 추가적으로 도로를 간단한 선뿐만 아니라 도로의 차선을 표시하는 추가 기능을 가진다.

public class RoadDisplay{
   public void draw(){
        System.out.println("기본 도로 표시");
   }
}

public class RoadDisplayWithLane extends RoadDisplay{
    public void draw(){
        super.draw();
        drawLane();        //추가적으로 차선을 표시
     }
    private void drawLane(){
        System.out.println("차선 표시");
    }
}

도로를 간단한 선으로 표시하는 RoadDisplay 클래스와 추가적으로 차선을 표시하는 RoadDisplayWithLane 클래스를 정의했다. RoadDisplayWithLane 클래스는 도로 표시 기능도 제공하므로 RoadDisplay 클래스의 하위 클래스로 구현했다.

 

 

- 문제점

또 다른 도로 표시 기능인 교통량 표시와 교차로 표시가 추가 되었다. 이 추가된 여러 가지 기능들을 조합해 제공하고 싶으면 어떻게 해야 할까? 예를 들어, 기본 도로 표시에 차선 표시 기능과 교통량 표시 기능을 함게 제공하고 싶다면 어떻게 해야 할까? 이럴 경우 RoadDisplayWithLane 클래스와 같이 차선 표시 기능과 교통량 표시 기능을 가지는 RoadDisplay의 하위 클래스 RoadDisplayWithLaneTraffic클래스를 추가하는 것이 적절할 수 도 있다. 하지만 다양한 기능의 조합을 고려해야 하는 경우 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해야 하는 단점이 있다. 차선 표시 기능, 교통량 표시 기능, 교차로 표시 기능이 있고 이들을 조합할 시 최대 7가지의 RoadDisplay 하위 클래스를 추가해야 한다. 이에 대한 해결책으로 데코레이터 패턴을 사용하면 된다.

 

 

- 해결책


public abstract class Display{ public abstract void draw(); } public class RoadDisplay extends Display{ public void draw(){ System.out.println("기본 도로 표시"); } } public abstract class DisplayDecorator extends Display { private Display decoratedDisplay; public DisplayDecorator(Display decoratedDisplay){ this.decoratedDisplay = decoratedDisplay; } public void draw(){ decoratedDisplay.draw(); } } public class LaneDecorator extends DisplayDecorator{ public LaneDecorator(Display decoratedDisplay){ super(decoratedDisplay); } public void draw(){ super.draw(); drawLane(); } private void drawLane(){ System.out.println("차선 표시"); } } public abstract class DisplayDecorator extends Display { private Display decoratedDisplay; public DisplayDecorator(Display decoratedDisplay){ this.decoratedDisplay = decoratedDisplay; } public void draw(){ decoratedDisplay.draw(); } } public class TrafficDecorator extends DisplayDecorator{ public TrafficDecorator(Display decoratedDisplay){ super(decoratedDisplay); } public void draw(){ super.draw(); drawTraffic(); } private void drawTraffic(){ System.out.println("교통량 표시"); } }
public class Client{
   public static void main(String[] args){
        Display road = new RoadDisplay();
        road.draw()     //기본 도로 표시
      
        Display roadWithLane = new LaneDecorator(new RoadDisplay());
        roadWithLane.draw();    //기본 도로 표시 + 차선 표시
    
        Display roadWithLaneTraffic = new TrafficDecorator(new LaneDecorator(new RoadDisplay()));
        roadWithLaneTraffic.draw();     //기본 도로 표시 + 차선 표시 + 교통량 표시
   }
}

위와 같이 각 추가 기능을 Decorator 클래스로 정의한 후 필요한 Decorator 객체를 조합함으로서 추가 기능의 조합을 구현했다.

'자바 > 디자인패턴' 카테고리의 다른 글

퍼사드 패턴  (0) 2017.06.08
프로토타입 패턴  (0) 2017.06.08
컴퍼지트 패턴  (0) 2017.06.07
옵저버(Observer) 패턴(2) - 직접 구현  (0) 2017.06.06
옵저버(Observer) 패턴(1) - JDK API 활용  (0) 2017.06.06

+ Recent posts