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



ex) 최종 MyImage.h 파일(LoadImage, SaveImage는 다음에 배움)

 

#pragma once
#include <windows.h>
#include  <assert.h>
#define CLIP(x) (x < 0)? 0 : x > 255 ? 255 : x

template <typename T>
class CMyImage
{
public:
	CMyImage(void)
	: m_nChannels(0)
	, m_nHeight(0)
	, m_nWidth(0)
	, m_nWStep(0)
	, m_pImageData(NULL)
	{}

	CMyImage(int nWidth, int nHeight, int nChannels = 1)
		: m_nChannels(nChannels)
		, m_nHeight(nHeight)
		, m_nWidth(nWidth)
		, m_nWStep(((nWidth*nChannels * sizeof(T) + 3)&~3) / sizeof(T))
	{
		m_pImageData = new T[m_nHeight*m_nWStep];
	}

	CMyImage(const CMyImage& myImage)
	{
		m_nChannels = myImage.m_nChannels;
		m_nHeight = myImage.m_nHeight;
		m_nWidth = myImage.m_nWidth;
		m_nWStep = myImage.m_nWStep;
		m_pImageData = new T[m_nHeight*m_nWStep];
		memcpy(m_pImageData, myImage.m_pImageData, m_nHeight*m_nWStep * sizeof(T));
	}

	CMyImage& operator=(const CMyImage& myImage)
	{
		if (this == &myImage)
			return *this;

		m_nChannels = myImage.m_nChannels;
		m_nHeight = myImage.m_nHeight;
		m_nWidth = myImage.m_nWidth;
		m_nWStep = myImage.m_nWStep;

		if (m_pImageData)
			delete[] m_pImageData;

		if (myImage.m_pImageData != NULL)
		{
			m_pImageData = new T[m_nHeight*m_nWStep];
			memcpy(m_pImageData, myImage.m_pImageData, m_nHeight*m_nWStep * sizeof(T));
		}
		return *this;
	}

	template <typename From>
	CMyImage(const CMyImage<From>& myImage)
	{
		m_nChannels = myImage.GetChannel();
		m_nHeight = myImage.GetHeight();
		m_nWidth = myImage.GetWidth();
		m_nWStep = ((m_nWidth*m_nChannels * sizeof(T) + 3)&~3) / sizeof(T);
		m_pImageData = new T[m_nHeight*m_nWStep];

		int nWStep = myImage.GetWStep();

		if (sizeof(T) == 1)
		{
			for (int r = 0; r<m_nHeight; r++)
			{
				T* pDst = GetPtr(r);
				From* pSrc = myImage.GetPtr(r);
				for (int c = 0; c<nWStep; c++)
				{
					pDst[c] = (T)CLIP(pSrc[c]);
				}
			}
		}
		else
		{
			for (int r = 0; r<m_nHeight; r++)
			{
				T* pDst = GetPtr(r);
				From* pSrc = myImage.GetPtr(r);
				for (int c = 0; c<nWStep; c++)
				{
					pDst[c] = (T)pSrc[c];
				}
			}
		}
	}

	void SetConstValue(T val)
	{
		if (val == 0)   // 조건 (1)
		{
			memset(m_pImageData, 0, m_nWStep*m_nHeight * sizeof(T));
			return;
		}

		if (sizeof(T) == 1)  // 조건 (2)
		{
			memset(m_pImageData, val, m_nWStep*m_nHeight);
		}
		else  // 나머지 경우들
		{
			T* pData = m_pImageData;
			for (int r = 0; r<m_nHeight; r++)
			{
				for (int c = 0; c<m_nWidth; c++)
				{
					pData[c] = val;
				}
				pData += m_nWStep;
			}
		}
	}


	bool IsEmpty() const
	{
		return m_pImageData ? false : true;
	}


	~CMyImage(void) 
	{
		if (m_pImageData)
		{
			delete[] m_pImageData;
		}
	}


	bool LoadImage(const char* filename)
	{
		assert(sizeof(T) == 1); // BYTE형의 경우만 가능

		if (strcmp(".BMP", &filename[strlen(filename) - 4]))
		{
			FILE* pFile = NULL;
			fopen_s(&pFile, filename, "rb"); // 바이너리 읽기 모드
			if (!pFile)
				return false;

			BITMAPFILEHEADER fileHeader;

			if (!fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, pFile))
			{
				fclose(pFile);
				return false;
			}

			if (fileHeader.bfType != 0x4D42) // 'BM' 문자 검사
			{
				fclose(pFile);
				return false;
			}

			BITMAPINFOHEADER infoHeader;
			if (!fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, pFile))
			{
				fclose(pFile);
				return false;
			}

			if (infoHeader.biBitCount != 8 && infoHeader.biBitCount != 24)
			{
				fclose(pFile);
				return false;
			}

			if (m_nWidth != infoHeader.biWidth && m_nHeight != infoHeader.biHeight
				&& m_nChannels != infoHeader.biBitCount / 8)
			{
				if (m_pImageData)
					delete[] m_pImageData;

				m_nChannels = infoHeader.biBitCount / 8;
				m_nHeight = infoHeader.biHeight;
				m_nWidth = infoHeader.biWidth;
				m_nWStep = (m_nWidth*m_nChannels * sizeof(T) + 3)&~3;

				m_pImageData = new T[m_nHeight*m_nWStep];
			}

			fseek(pFile, fileHeader.bfOffBits, SEEK_SET);

			int r;
			for (r = m_nHeight - 1; r >= 0; r--)
			{
				if (!fread(&m_pImageData[r*m_nWStep], sizeof(BYTE), m_nWStep, pFile))
				{
					fclose(pFile);
					return false;
				}
			}

			fclose(pFile);

			return true;
		}
		else
		{
			return false;
		}
	}

	bool SaveImage(const char* filename)
	{
		assert(sizeof(T) == 1); // BYTE형의 경우만 가능

		if (strcmp(".BMP", &filename[strlen(filename) - 4]))
		{
			FILE* pFile = NULL;
			fopen_s(&pFile, filename, "wb");
			if (!pFile)
				return false;

			BITMAPFILEHEADER fileHeader;
			fileHeader.bfType = 0x4D42; // 'BM'
			fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_nWStep*m_nHeight + (m_nChannels == 1) * 1024;
			fileHeader.bfReserved1 = 0;
			fileHeader.bfReserved2 = 0;
			fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (m_nChannels == 1) * 256 * sizeof(RGBQUAD);

			fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, pFile);

			BITMAPINFOHEADER infoHeader;
			infoHeader.biSize = sizeof(BITMAPINFOHEADER);
			infoHeader.biWidth = m_nWidth;
			infoHeader.biHeight = m_nHeight;
			infoHeader.biPlanes = 1;
			infoHeader.biBitCount = m_nChannels * 8;
			infoHeader.biCompression = BI_RGB;
			infoHeader.biSizeImage = m_nWStep*m_nHeight;
			infoHeader.biClrImportant = 0;
			infoHeader.biClrUsed = 0;
			infoHeader.biXPelsPerMeter = 0;
			infoHeader.biYPelsPerMeter = 0;

			fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, pFile);

			if (m_nChannels == 1)
			{
				for (int l = 0; l<256; l++)
				{
					RGBQUAD GrayPalette = { l, l, l, 0 };
					fwrite(&GrayPalette, sizeof(RGBQUAD), 1, pFile);
				}
			}

			int r;
			for (r = m_nHeight - 1; r >= 0; r--)
			{
				fwrite(&m_pImageData[r*m_nWStep], sizeof(BYTE), m_nWStep, pFile);
			}

			fclose(pFile);
			return true;
		}
		else
		{
			return false;
		}
	}



	inline T& GetAt(int x, int y, int c = 0) const
	{
		assert(x >= 0 && x < m_nWidth && y >= 0 && y < m_nHeight);
		return m_pImageData[m_nWStep*y + m_nChannels*x + c];
	}

	int GetChannel()	const { return m_nChannels; }
	int GetHeight()		const { return m_nHeight; }
	int GetWidth()		const { return m_nWidth; }
	int GetWStep()		const { return m_nWStep; }
	T* GetPtr(int r = 0, int c = 0)		const { return m_pImageData + r*m_nWStep + c; }

protected:
	int		m_nChannels;	// 채널 수
	int		m_nHeight;		// 세로 픽셀 수
	int		m_nWidth;		// 가로 픽셀 수
	int		m_nWStep;		// 행당 데이터 원소 수
	T*		m_pImageData;	// 픽셀 배열 포인터
};

typedef CMyImage<BYTE> CByteImage;
typedef CMyImage<int> CInteImage;
typedef CMyImage<float> CFloatImage;
typedef CMyImage<double> CDoubleImage;

 

<Main>

 

// TestImage.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"
#include "CMyImage\MyImage.h"

int main()
{
	CByteImage image1(640, 480); // 생성자 호출
	image1.SetConstValue(0);	 // 0으로 초기화(운영체제 시스템에 따라서 이상한 값이 들어갈 수 있다)

	CByteImage image2(image1); // 복사 생성자 호출
	CByteImage image3;		   // 기본 생성자 호출
	image3 = image1;		   // 대입 연산자 호출

	int nWidth = image1.GetWidth();   // 영상의 너비
	int nHeight = image1.GetHeight();  // 영상의 높이
	int nChannel = image1.GetChannel(); // 영상의 채널 수

	double incX = 255.0 / nWidth; // 255를 나눈 이유는 밑에 있는 그라디에션에서 BYTE형이기 때문에 255를 넘지 않기 위해서
	double incY = 255.0 / nHeight;
	int r, c;
	for (r = 0; r<nHeight; r++) // 행 단위 이동
	{
		for (c = 0; c<nWidth; c++) // 열 단위 이동
		{
			image2.GetAt(c, r) = (BYTE)(c*incX); // 가로 그라데이션
			image3.GetAt(c, r) = (BYTE)(r*incY); // 세로 그라데이션
		}
	}

	// 결과 영상 저장
	image1.SaveImage("Black.bmp");
	image2.SaveImage("GradationX.bmp");
	image3.SaveImage("GradationY.bmp");

    return 0;
}

<결과>

왼쪽은 image2이고 오른쪽은 image3이다.

+ Recent posts