작년 9월쯤, 어떻게 하다보니 회사 내부 아는 분들의 부탁으로 작성했었던 프로그램입니다. 그냥 과제를 해야 하는데 프로젝트 때문에 시간이 없다보니 제가 하게 되어 만들었던 프로그램이죠. 비트맵이라는 것이 실질적으로 그림정보를 들고 있는 것이기 때문에 아주 복잡한 기능은 적용하지 않고 간단한 기능들을 추가해보았습니다.
프로그램 모습은 위 그림과 같습니다. 대충 메뉴를 소개하자면 아래와 같습니다.
기능은 간단하게 1명당 1개의 기능으로 구성을 했었기 때문에 명도, 블러, 경계값, 반전, 모자이크 기능을 넣었었죠. 그럼 기능을 수행하면 어떻게 되는지 한 번 살펴보겠습니다.
명도를 선택하면 다음과 같이 RGB 전체의 값을 일정 크기만큼 증가시키는 기능으로 구현했습니다. 코드는 다음과 같죠. 실제적으로 명도를 증가시키려면 HSI 로 나눈 뒤, 작업을 해야 할 것입니다.
CBrightness.cpp (Language : cpp)
void CBrightness::Convert2Filter ( DWORD *inData, DWORD *OurData, int RawWidth, int RawHeight )
{
this->ImgWidth = RawWidth;
this->ImgHeight = RawHeight;
// 설정 창을 표시 하고 BlurFactor 에 세팅한 다음 작업한다.
if ( -150 <= InputFactor && InputFactor <= 150 ) {
AtomFactor = InputFactor / 3 ;
for ( int i = 0 ; i < RawHeight; i++ ) {
for ( int j = 0 ; j < RawWidth; j++ ) {
ColorUnion ColorData = GetBrightnessPixel( inData, j, i ) ;
SetPixel( OurData, j, i, ColorData ) ;
}
}
}
}
ColorUnion CBrightness::GetBrightnessPixel ( DWORD *inData, int X, int Y )
{
ColorUnion ColorData = GetPixel( inData, X, Y ) ;
int Red = ColorData.ColorPixel .Red , Green = ColorData.ColorPixel .Green , Blue = ColorData.ColorPixel .Blue ;
Red += AtomFactor;
Green += AtomFactor;
Blue += AtomFactor;
if ( Red < 0 )
Red = 0 ;
else if ( 254 < Red )
Red = 254 ;
if ( Green < 0 )
Green = 0 ;
else if ( 254 < Green )
Green = 254 ;
if ( Blue < 0 )
Blue = 0 ;
else if ( 254 < Blue )
Blue = 254 ;
return MakePixel( 0xFF, Red, Green, Blue ) ;
}
복잡한것 같지만 32비트 이미지를 1바이트 마다 나누어 RGB 값을 일정 크기만큼 더해주는 기능입니다.
다음은 블러기능입니다. 보통 포토샵을 보면 뽀샤시 효과를 위해서 자주 사용하는 필터죠. 사실 블러에는 가우시안 블러, 모션 블러를 비롯해서 많은 종류가 존재하지만 위 같은 경우엔 그냥 뿌옇게 흐리는 기능만을 구현했죠.
CBlur.cpp (Language : cpp)
void CBlur::Convert2Filter ( DWORD *inData, DWORD *OurData, int RawWidth, int RawHeight )
{
this->ImgWidth = RawWidth;
this->ImgHeight = RawHeight;
// 설정 창을 표시 하고 BlurFactor 에 세팅한 다음 작업한다.
for ( int i = 0 ; i < RawHeight; i++ ) {
for ( int j = 0 ; j < RawWidth; j++ ) {
ColorUnion ColorData = GetBlurPixel( inData, j, i ) ;
SetPixel( OurData, j, i, ColorData ) ;
}
}
}
ColorUnion CBlur::GetBlurPixel ( DWORD *inData, int X, int Y )
{
int Red = 0 , Green = 0 , Blue = 0 ;
ColorUnion OriginalColor = GetPixel( inData, X, Y ) ;
for ( int i = Y - InputFactor; i <= Y + InputFactor; i++ ) {
for ( int j = X - InputFactor; j <= X + InputFactor; j++ ) {
if ( IsContain( j, i ) ) {
ColorUnion ColorData = GetPixel( inData, j, i ) ;
Red += ColorData.ColorPixel .Red ;
Green += ColorData.ColorPixel .Green ;
Blue += ColorData.ColorPixel .Blue ;
}
else {
Red += OriginalColor.ColorPixel .Red ;
Green += OriginalColor.ColorPixel .Green ;
Blue += OriginalColor.ColorPixel .Blue ;
}
}
}
int DividerNumber = static_cast< int >( std::pow ( 2 * InputFactor + 1 , 2.0 ) ) ;
Red /= DividerNumber;
Green /= DividerNumber;
Blue /= DividerNumber;
return MakePixel( 0xFF, Red, Green, Blue ) ;
}
일정 범위의 픽셀에 대하여 RGB값을 서로 더하여 평균값을 구하는 것인데 물론 모자이크 필터가 아니다보니 가중치를 주어 원래 자기의 색이 다른 픽셀의 값에 밀리지 않도록 구현을 해주어야 합니다.
이 것 또한 포토샵에서 볼 수 있는 Threshold 필터 입니다. 이 것은 어떠한 점에 존재하는 픽셀값이 일정 값 이상이면 하얀색 아닐 경우 검은색으로 표시하는 기능입니다. 보통 이미지패턴 처리를 위하여 자주 사용하는 기본 필터이기도 하죠.
CThreshold.cpp (Language : cpp)
void CThreshold::Convert2Filter ( DWORD *inData, DWORD *OurData, int RawWidth, int RawHeight )
{
this->ImgWidth = RawWidth;
this->ImgHeight = RawHeight;
// 쓰레숄드 값 세팅 입력 받고
for ( int i = 0 ; i < RawHeight; i++ ) {
for ( int j = 0 ; j < RawWidth; j++ ) {
ColorUnion ColorData = GetThresholdColor( inData, j, i ) ;
SetPixel( OurData, j, i, ColorData ) ;
}
}
}
ColorUnion CThreshold::GetThresholdColor ( DWORD *inData, int X, int Y )
{
int AvgData = 0 ;
ColorUnion ColorData = GetPixel( inData, X, Y ) ;
AvgData = ( ColorData.ColorPixel .Red + ColorData.ColorPixel .Green + ColorData.ColorPixel .Blue ) / 3 ;
if ( AvgData >= InputFactor )
ColorData = MakePixel( 0xFF, 0xFF, 0xFF, 0xFF ) ;
else
ColorData = MakePixel( 0x00, 0x00, 0x00, 0x00 ) ;
return ColorData;
}
Invert 필터입니다. 바로 반전 기능이죠. 뭔가 느낌이 그로테스크 하지 않나요?^^ 사실 이 기능은 위 쓰레숄드 만큼이나 구현하기 쉽습니다. 왜냐하면 RGB 값을 바로 ~ 연산 취하면 되기 때문이죠.
CInvert.cpp (Language : cpp)
void CInvert::Convert2Filter ( DWORD *inData, DWORD *OurData, int RawWidth, int RawHeight )
{
this->ImgWidth = RawWidth;
this->ImgHeight = RawHeight;
for ( int i = 0 ; i < RawHeight; i++ ) {
for ( int j = 0 ; j < RawWidth; j++ ) {
ColorUnion ColorData = GetPixel( inData, j, i ) ;
ColorData.ColorPixel .Red = ~ColorData.ColorPixel .Red ;
ColorData.ColorPixel .Green = ~ColorData.ColorPixel .Green ;
ColorData.ColorPixel .Blue = ~ColorData.ColorPixel .Blue ;
SetPixel( OurData, j, i, ColorData ) ;
}
}
}
마지막으로 모자이크 필터입니다. 이 것 또한 대충 구현하는 방법이 짐작되시죠?^^ 위에 언급한 블러 필터에서 모자이크 필터 얘기가 나왔으므로 아마 다들 짐작하실 것으로 생각합니다. 약간의 응용만 하시면 됩니다.
CMosaic.cpp (Language : cpp)
void CMosaic::Convert2Filter ( DWORD *inData, DWORD *OurData, int RawWidth, int RawHeight )
{
this->ImgWidth = RawWidth;
this->ImgHeight = RawHeight;
// 모자이크 팩터 입력 받고
for ( int i = 0 ; i < RawHeight; i += InputFactor ) {
for ( int j = 0 ; j < RawWidth; j += InputFactor ) {
ColorUnion ColorData = GetMosaicPixel( inData, j, i ) ;
for ( int YIdx = i; YIdx < i + InputFactor; YIdx++ ) {
for ( int XIdx = j; XIdx < j + InputFactor; XIdx++ ) {
if ( IsContain( XIdx, YIdx ) )
SetPixel( OurData, XIdx, YIdx, ColorData ) ;
}
}
}
}
}
ColorUnion CMosaic::GetMosaicPixel ( DWORD *inData, int X, int Y )
{
int Red = 0 , Green = 0 , Blue = 0 ;
ColorUnion OriginalColor = GetPixel( inData, X, Y ) ;
for ( int i = Y; i < Y + InputFactor; i++ ) {
for ( int j = X; j < X + InputFactor; j++ ) {
if ( IsContain( j, i ) ) {
ColorUnion ColorData = GetPixel( inData, j, i ) ;
Red += ColorData.ColorPixel .Red ;
Green += ColorData.ColorPixel .Green ;
Blue += ColorData.ColorPixel .Blue ;
}
else {
Red += OriginalColor.ColorPixel .Red ;
Green += OriginalColor.ColorPixel .Green ;
Blue += OriginalColor.ColorPixel .Blue ;
}
}
}
int DividerNumber = static_cast< int >( std::pow ( InputFactor, 2.0 ) ) ;
Red /= DividerNumber;
Green /= DividerNumber;
Blue /= DividerNumber;
return MakePixel( 0xFF, Red, Green, Blue ) ;
}
뭔가 복잡한 것 같지만 이러한 이미지 처리를 하는 것으로 다양한 결과물을 만들어 낼 수 있습니다. 게임 같은 경우 텍스쳐 또한 각도에 따라서 이미지 크기를 변경시키고 회전시켜 좌표에 맵핑을 하는 것이죠. 그래서 실제 게임 플레이어같은 경우엔 어떠한 형태를 가진 물체로 인식하게 하는 것입니다.
이 글을 보신분 이미지 크기를 변경시키는 Resizing 기능을 구현해보시는 것은 어떻습니까?^^
=뱀다리=
std::pow를 사용하는 것을 생성자에 집어넣고 하여 최적화를 시켜야 할 여지가 존재합니다. 다만 당시 빠르게 작성하느라 못했기 때문에 최적화는 필요하신 분이 하셔서 사용해보세요^^
크리에이티브 커먼즈 라이센스
트랙백 주소 :: http://www.filewiki.net/tc/trackback/44
댓글을 달아 주세요