이번 과제는 OpenCV에서 Gaussian Filtering과 down-sampling을 이용하여


DoG Channel을 생성하고 feature point를 찾은 뒤에 이를 이미지에 표시하는 것이다.


이번에는 샘플 코드가 없었다.


대충 무슨 용도인지는 알겠는데 수업을 제대로 안들었는지 해깔린다.


OpenCV를 이용해서 Gaussian Filtering과 Down-Sampling을 사용할 수 있고 나머지는 직접 구현해야 한다.


#define Octave 7
#define Gaussian 6

struct OctaveGroup
{
	Mat gaussianFliter[Gaussian];
	Mat DoGChannel[Gaussian - 1];
};


우선 다음과 같은 Struct를 만들었다. 한 옥타브 내에는 가우시안 필터와 가우시안 필터-1개의 DoG Channel이 있다.


	OctaveGroup octave[Octave];

	Mat image = imread("img1.bmp");

	Mat gray;//grayscale 이미지를 저장할 Mat

	cvtColor(image, gray, CV_BGR2GRAY);


main 함수에서 설정된 Octave 값만큼 ocatave를 생성하고 이미지를 불러온 후, grayscale 값으로 변환한다.


	double beforeSig;

	for (int i = 0; i < Octave; ++i)
	{
		for (int j = 0; j < Gaussian; ++j)
		{
			double sig;
			int size;

			if (j == 0)
			{
				if (i != 0)//0옥타브가 아닐 경우
				{
					pyrDown(octave[i - 1].gaussianFliter[3], octave[i].gaussianFliter[j],
						Size(octave[i - 1].gaussianFliter[3].cols / 2, octave[i - 1].gaussianFliter[3].rows / 2));
					beforeSig = 1.6;//Sig는 1.6이었던 것으로 가정.
				}
				else//0 옥타브일 경우, 원본 이미지에 sig 1.6 값을 이용하여 필터링.
				{
					sig = 1.6;//첫번째 이미지는 Sig가 1.6.
					size = CheckSigSize(sig);//Sig 값에 따른 마스크 사이즈 확인
					GaussianBlur(gray, octave[i].gaussianFliter[j], Size(size, size), sig, sig);//가우시안 블러.
					beforeSig = sig;//Sig 기록
				}
			}
			else
			{
				sig = beforeSig * pow(2.0, 1.0 / 3.0);//Sig_n+1 = Sig_n * k     k = 2^(1/3)에 대한 코드
				double currentSig = sqrt(pow(sig, 2) - pow(beforeSig, 2));//sqrt(Sig_n+1^2 - Sig_n^2)에 대한 코드.
				size = CheckSigSize(currentSig);//Sig 값에 따른 마스크 사이즈 확인
				GaussianBlur(octave[i].gaussianFliter[j - 1], octave[i].gaussianFliter[j], Size(size, size), currentSig, currentSig);//이전 가우시안 필터를 기반으로 한 가우시안 블러.
				beforeSig = sig;//Sig 기록
			}
		}
	}


원본 이미지를 기반으로 GaussiangFilter를 할 경우, Sigma가 커질 때마다 점점 느려진다고 한다.


이를 해결하기 위하여 위와 같이 sqrt(현 시그마 값^2 - 이전 시그마 값^2)을 currentSig에 삽입한다.


CheckSigSize는 반복적으로 사용되어서 따로 함수로 분류하였다.



int CheckSigSize(double sig)//Sig를 이용하여 값을 계산해야 Size를 계산해야 되는데 빈번하게 쓰여서 따로 함수를 묶음.
{
	int size;//출력할 마스크 사이즈

	size = ceil(6 * sig);//6*sig에 올림수를 마스크 사이즈로 임시 설정

	if (size % 2 == 0)//만약 짝수일 경우
		size++;//1을 더함

	return size;//마스크 사이즈 return;
}



위 내용을 기반으로 마스크 사이즈를 결정하는 함수를 만들었다.


	for (int i = 0; i < Octave; ++i)
	{
		for (int j = 0; j < Gaussian-1; ++j)
		{
			ostringstream name;
			name << "DoG" << i << j << ".bmp";
			imwrite(name.str(), octave[i].DoGChannel[j]);
		}
	}


테스트용 파일을 출력하고자 하였는데 string 변수를 수정해서 넣으면 자꾸 에러가 발생한다.


ostringstream을 쓰면 된다는 글을 참고하여 작성했다.


https://stackoverflow.com/questions/27320812/how-to-save-cvimwrite-in-different-name-in-the-loop


	for (int i = 0; i < Octave; ++i)//모든 옥타브에서 처리.
	{
		for (int j = 0; j < Gaussian - 1; ++j)//DoG 채널의 개수는 Gaussian - 1
		{
			//octave[i].DoGChannel[j] = octave[i].gaussianFliter[j + 1] - octave[i].gaussianFliter[j];
			cv::absdiff(octave[i].gaussianFliter[j + 1], octave[i].gaussianFliter[j], octave[i].DoGChannel[j]);//DoG채널 생성. DoG[n] = G[n+1] - G[n]. 위의 식 보다 결과값이 더 좋아서 사용.
		}
	}

원래 DoG[n] = Gaussian[n+1] - Gaussian[n] 으로 알고 있는데, 이럴 경우 결과에서 극점이 뭉치는 현상이 있다.


이유는 모르겠지만 더 적합한 함수가 있다고 하여 사용했다.


	for (int o = 0; o < Octave; ++o)//모든 옥타브에서 실행.
	{
		for (int d = 1; d < Gaussian - 2; ++d)//DoG를 비교할 때, 0번째와 맨 마지막은 사용하지 않는다. DoG개수는 Gaussian-1이므로 Gaussian-2로 설정
		{
			for (int i = 1; i < octave[o].DoGChannel[d].rows - 1; ++i)//첫 열과 끝 열, 첫 행과 끝 행은 기준으로 삼지 않는다.
			{
				for (int j = 1; j < octave[o].DoGChannel[d].cols - 1; ++j)
				{
					int neighbor[26];//26개의 이웃값을 삽입할 배열
					int n = 0;//배열 번호.
					int value = octave[o].DoGChannel[d].at<uchar>(i, j);//현재 기준의 값.

					for (int t = -1; t <= 1; ++t)//26개는 각각 채널별로 3x3 공간의 값을 받아온다. 자신을 기준으로 -1부터 +1까지의 값을 받아오므로 다음과 같은 for문으로 대입.
					{
						for (int tt = -1; tt <= 1; ++tt)
						{
							neighbor[n++] = octave[o].DoGChannel[d - 1].at<uchar>(i + t, j + tt);//기준 위치보다 상위 DoG채널의 값 받아오기.
							neighbor[n++] = octave[o].DoGChannel[d + 1].at<uchar>(i + t, j + tt);//기준 위치보다 하위 DoG채널의 값 받아오기.
							if (t != 0 || tt != 0)//기준과 같은 위치의 값은 배열에 넣지 않는다.
							{
								neighbor[n++] = octave[o].DoGChannel[d].at<uchar>(i + t, j + tt);//기준 DoG채널의 값 받아오기
							}
						}
					}

					int max = neighbor[0];//max, min에 초기값으로 첫 neighbor값 삽입.
					int min = neighbor[0];

					for (int x = 1; x < 26; ++x)//min, max 비교
					{
						if (max < neighbor[x])
						{
							max = neighbor[x];
						}
						else if (min > neighbor[x])
						{
							min = neighbor[x];
						}
					}
					if (value > max || value < min)//극정일 경우
					{
						circle(image, Point(j*pow(2, o), i*pow(2, o)), 1.6*pow(2, (o + d) / 3.0), Scalar(0, 255, 0), 1);//원 그리기. 반지름은 scale 값이며 1.6*2^((Octave num + DoGlayer Num)/3.0)
					}
				}
			}
		}
	}

	imwrite("Result.bmp", image);//이미지 출력


각 위치에서 26개의 이웃값을 받아오고 max, min 비교 후에 극점 여부를 확인.


극점일 경우 이미지에 원을 그린다. 원을 그릴 때는


옥타브 값과 DoG 채널 값을 받아와서 Scale 값으로 변환 후, 이를 반지름으로 사용하여 그린다.




옥타브 값 2(0, 1)과 가우시안 채널 6(0~5)의 결과값.



전체 코드


남는 학점 채우려고 했던 과목이었지만 재밌었다.


아이디어도 중요하지만 섬세함이 많이 필요한 것이 디자인이라는 생각이 많이 들었다.


지금보면 어딘가 부족해보이는 부분은 내가 섬세함이 부족했기 때문이라는 생각이 든다.



주제 : 홍보물 제작


캡콤, SEED9, CJ >> 마계촌 온라인 포스터의 밑부분을 활용한 것 같다. 지금은 망했다.


주변에 둥실둥실 떠다니는 아이콘과 부처 뒤의 나무가 조금 아쉽다


색감 보정에 대한 강의 후에 만든 과제였을 것이다.



주제 : 전자제품 홍보


예전부터 나는 엑스페리아 시리즈가 좋았다. 


아이폰은 4부터 5S까지의 각진 디자인이 좋았고 엑스페리아는 언제나 각진 디자인이라 좋았다.


XZ2가 곡면 디자인을 넣었으니 성능 안좋고 디자인도 못생긴 폰이 되어버렸다.



과제 주제 : 브랜드의 크리스마스 이미지 만들기


안드로이드 마스코트는 다양한 이미지가 있어서 사용하기 좋다.



주제 : 스마트폰 특정 브랜드의 위젯, 인터페이스 및 잠금화면 제작.


iOS는 해볼 수 없는 커스텀 인터페이스다. 이래서 안드로이드가 좋다.


안드로이드의 Smart Launcher를 대학교 입학할 때 구매한 이후로 지금까지 사용하고 있다.


https://play.google.com/store/apps/details?id=ginlemon.flowerpro


간략하면서 보기 좋은 UI가 매력적이다.


PSP의 XMB도 폰으로 쓰기에 괜찮다고 생각한다. 


옆으로 넘기고 위아래로 넘기고.. 나중에 만들어보고 싶다. 



주제 : 모바일 게임 및 UI 디자인


이런 디자인의 게임이면 망할 것이다..



주제 : 캐릭터를 활용한 포스터


반창고 부분은 이름이 들어간 부분이다.


지금보니 되게 성의없어 보인다.



주제 : 캐릭터와 패스를 활용한 디자인


왜 햄토리 였을까?



주제 : 픽셀아트와 손글씨를 이용한 포스터 디자인


SHURE SRH840을 막 샀을 때라 헤드폰에 관심이 좀 있었던 것 같다.




지금보니 뒤로 갈수록 점점 성의없어 보인다.

'호랑이 담배피던 시절에' 카테고리의 다른 글

2014.03 ~ 2014. 06 2D그래픽디자인  (0) 2018.04.19
2011.04.04.  (0) 2018.03.26
[Prezi] CG를 꿈꾸다  (0) 2018.03.26

참고 : https://forum.unity.com/threads/shadow-catcher-or-matte-shadow-shader.414418/


홀로렌즈는 MR 컨텐츠를 즐기기 위한 장비이다.


현실 공간에 가상의 오브젝트를 띄우는데, 그림자가 보이지 않는다면 이질감을 주게 된다.


위의 참고 글을 보면 다음과 같은 소스 코드가 있다.



Shader "Unlit/VertColorWithShadow"
{
    Properties
    {
        _MainColor("Main Color", COLOR) = (0,0,0,1)
        _ShadowColor("Shadow Color", COLOR) = (1,0,0,1)
    }
 
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
 
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
 
            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag
 
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
 
            float4 _MainColor;
            float4 _ShadowColor;
 
            struct appdata
            {
                float4 vertex : POSITION;
            };
 
            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 mainColor : COLOR;
                LIGHTING_COORDS(1,2)
            };
         
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.mainColor = _MainColor;
 
                TRANSFER_VERTEX_TO_FRAGMENT(o);
 
                return o;
            }
         
            fixed4 frag (v2f i) : SV_Target
            {
                fixed atten = 1.0 - LIGHT_ATTENUATION(i);
                fixed3 shadowColor = atten * _ShadowColor.rgb;
                fixed4 finalColor = i.mainColor + fixed4(shadowColor, 1.0);
 
                return finalColor;
            }
            ENDCG
        }
 
        // Pass to render object as a shadow caster
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }
     
            ZWrite On ZTest LEqual Cull Off
 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
 
            struct v2f {
                V2F_SHADOW_CASTER;
            };
 
            v2f vert( appdata_base v )
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }
 
            float4 frag( v2f i ) : SV_Target
            {
                return float4(1.0, 1.0, 1.0, 1.0);
            }
            ENDCG
        }
    }
}

위의 Shader 코드를 Unity에서 작성한다.


Material을 새로 만들고 Shader를 집어넣는다.


Shadow Color를 적절하게 조절해준다.


Spatial Mapping Manager의 Surface Material에 위의 Material을 넣어준다.


그러면 홀로렌즈로 볼 때는 그림자가 정상적으로 나오는 것을 볼 수 있다.


하지만 디바이스 포탈을 통해 캡처나 녹화를 하면 다음과 같이 나온다.



공간이 어둡게 된 부분은 Spatial Mapping이 된 부분이고 밝은 부분은 Mapping이 되지 않은 부분이다.


홀로렌즈 시연을 할 때, 디바이스 포탈이나 스펙데이터 뷰를 사용한 녹화가 필요한데 이런 현상이 일어나면 곤란하다.


위의 코드 일부를 수정하면 간단하게 해결된다.

            fixed4 frag (v2f i) : SV_Target
            {
                fixed atten = 1.0 - LIGHT_ATTENUATION(i);
		if(atten > 0)
		{
 	               fixed3 shadowColor = atten * _ShadowColor.rgb;
        	       fixed4 finalColor = i.mainColor + fixed4(shadowColor, 1.0);
 
 	               return finalColor;
		}
		else
			return float4(0, 0, 0, 0);
            }





그림자 색감을 좀 더 부드럽고 퍼지는 느낌을 주고 싶지만 아직은 홀로렌즈의 성능이 낮아서 불가능하다.


전체 코드


Spatial Mapping에 있는 기능 때문인건지 InputManager의 기능인 것인지는 모르겠지만


OnInputClicked가 실행되면 Spatial Mapping의 Material이 켜지거나 꺼진다.


이를 해결하는 다양한 방법이 있겟지만 다음과 같은 방법을 이용하여 해결하였다.



SpatialMappingManager의 해당 부분을 drawVisualMeshes = value; 부분을 주석처리 하면 된다.


내부를 건드려서 좋은 방법은 아닌 것 같지만 일단 다음과 같이 진행하였다.


다른 방법이 있으면 공유 부탁드립니다.

+ Recent posts

티스토리 툴바