성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>가우시안 함수의 이산형(discrete) 커널 값 생성</h1> <p> shader 글에서 다룬,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unity로 실습하는 Shader (7) - Blur (평균값, 가우스, 중간값) 필터 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11620'>http://www.sysnet.pe.kr/2/0/11620</a> </pre> <br /> 가우스 함수는,<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> ${ G(x) = \frac{1}{{\sigma \sqrt {2\pi } }}e^{{{ -{x}^2 } \mathord{\left/ \right. } {2\sigma ^2 }}}<br /> }$<br /> </div><br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> ${ G(x, y) = \frac{1}{{\sigma ^2 {2\pi } }}e^{{{ - \left( x^2 + y^2 \right) } \mathord{\left/ \right. } {2\sigma ^2 }}}<br /> }$<br /> </div><br /> <br /> 연속 함수이긴 하지만, 영상 처리에서는 pixel 좌표가 정수로 떨어지므로 이산값으로 처리해야 합니다. 가령, 1x7 크기의 가우스 분포 값을 구한다면 "-3, -2, -1, 0, 1, 2, 3" 값에 대해 위의 가우스 함수를 적용하면 됩니다.<br /> <br /> 따라서, σ = 1.0이고, x = [-3, -2, -1, 0, 1, 2, 3] 구간에 대해 가우스 분포를 다음의 C# 코드로 구할 수 있습니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; namespace gaussK { class Program { static void Main(string[] args) { int[] xArray = new int[] { -3, -2, -1, 0, 1, 2, 3 }; double sigma = 5.0; WriteHost(Gaussian(sigma, xArray)); int[] xyArray = xArray; WriteHost(Gaussian2D(sigma, xyArray)); } private static double[] Gaussian(double sigma, int[] xArray) { double[] result = new double[xArray.Length]; double c = 2.0 * sigma * sigma; double sc = 1.0 / (Math.Sqrt(2 * Math.PI) * sigma); for (int i = 0; i < xArray.Length; i++) { double xValue = xArray[i]; result[i] = sc * Math.Pow(Math.E, -(xValue * xValue) / c); } return result; } private static double[,] Gaussian2D(double sigma, int[] xyArray) { double[,] result = new double[xyArray.Length, xyArray.Length]; double c = 2 * sigma * sigma; double sc = 1.0 / (c * Math.PI); for (int i = 0; i < xyArray.Length; i++) { for (int j = 0; j < xyArray.Length; j++) { double xValue = xyArray[i]; double yValue = xyArray[j]; result[i, j] = sc * Math.Pow(Math.E, -(xValue * xValue + yValue * yValue) / c); } } return result; } private static void WriteHost(double[] dblArray) { foreach (double value in dblArray) { Console.WriteLine(value.ToString("#0.000000")); } } private static void WriteHost(double[,] dblArray) { for (int i = 0; i < dblArray.GetLength(0); i++) { for (int j = 0; j < dblArray.GetLength(1); j++) { Console.Write(dblArray[i, j].ToString("#0.000000") + ","); } Console.WriteLine(); } } } } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > /* 출력 결과: // sigma = 1, { -3, -2, -1, 0, 1, 2, 3 } 0.004432 0.053991 0.241971 0.398942 0.241971 0.053991 0.004432 0.000020,0.000239,0.001072,0.001768,0.001072,0.000239,0.000020, 0.000239,0.002915,0.013064,0.021539,0.013064,0.002915,0.000239, 0.001072,0.013064,0.058550,0.096532,0.058550,0.013064,0.001072, 0.001768,0.021539,0.096532,0.159155,0.096532,0.021539,0.001768, 0.001072,0.013064,0.058550,0.096532,0.058550,0.013064,0.001072, 0.000239,0.002915,0.013064,0.021539,0.013064,0.002915,0.000239, 0.000020,0.000239,0.001072,0.001768,0.001072,0.000239,0.000020, // sigma = 1, { -4, -3, -2, -1, 0, 1, 2, 3, 4 } 0.000134 0.004432 0.053991 0.241971 0.398942 0.241971 0.053991 0.004432 0.000134 0.000000,0.000001,0.000007,0.000032,0.000053,0.000032,0.000007,0.000001,0.000000, 0.000001,0.000020,0.000239,0.001072,0.001768,0.001072,0.000239,0.000020,0.000001, 0.000007,0.000239,0.002915,0.013064,0.021539,0.013064,0.002915,0.000239,0.000007, 0.000032,0.001072,0.013064,0.058550,0.096532,0.058550,0.013064,0.001072,0.000032, 0.000053,0.001768,0.021539,0.096532,0.159155,0.096532,0.021539,0.001768,0.000053, 0.000032,0.001072,0.013064,0.058550,0.096532,0.058550,0.013064,0.001072,0.000032, 0.000007,0.000239,0.002915,0.013064,0.021539,0.013064,0.002915,0.000239,0.000007, 0.000001,0.000020,0.000239,0.001072,0.001768,0.001072,0.000239,0.000020,0.000001, 0.000000,0.000001,0.000007,0.000032,0.000053,0.000032,0.000007,0.000001,0.000000, */ </pre> <br /> 재미있는 것은, "<a target='tab' href='http://www.sysnet.pe.kr/2/0/11620'>Unity로 실습하는 Shader (7) - Blur (평균값, 가우스, 중간값) 필터</a>" 글에서 소개한,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Gaussian Kernel Calculator ; <a target='tab' href='http://dev.theomader.com/gaussian-kernel-calculator/'>http://dev.theomader.com/gaussian-kernel-calculator/</a> </pre> <br /> 사이트의 결과와 다를 때가 많다는 점입니다. 일례로, sigma == 1.0, kernel size == 3일 때 이 글의 코드는 다음의 값을 출력하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0.241971 0.398942 0.241971 0.058550,0.096532,0.058550, 0.096532,0.159155,0.096532, 0.058550,0.096532,0.058550, </pre> <br /> "<a target='tab' href='http://dev.theomader.com/gaussian-kernel-calculator/'>Gaussian Kernel Calculator</a>" 글에서는 아래와 같이 다소 차이가 납니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0.27901 0.44198 0.27901 0.077847 0.123317 0.077847 0.123317 0.195346 0.123317 0.077847 0.123317 0.077847 </pre> <br /> 원인은 간단합니다. 제 코드에서는 sigma == 1.0일 때의 커널 합이 0.88288...인데, sigma 값에 따른 분포가 -1 ~ +1의 범위에서 1차원 가우시안 함수로는 (<a target='tab' href='http://onlinestatbook.com/2/calculators/normal_dist.html'>연속이 아닌</a>) 이산 값으로 약 88%만 점유하기 때문입니다. 즉, 나머지 12% 정도에 대한 값이 없어졌기 때문에 총합이 1이어야 하는 커널 마스크로는 부적합한 값이 생성된 것입니다.<br /> <br /> 이를 보완하는 방법으로는 sigma 값에 따라 커널 크기를 늘려 잡는 것이 있습니다. 가령, 가우시안 분포의 경우 -4σ ~ +4σ의 범위 값은 전체 분포의 약 99.99%를 차지하기 때문에 적절한 커널 크기로 σ의 4배 * 2배가 되어야 하며, 아울러 짝수가 아닌 홀수여야 하기 때문에 +1이 필요합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int recommendedSize = sigma * 4 * 2 + 1; </pre> <br /> sigma가 커질수록 가우시안 분포의 종 모양이 낮아지고 넓어지므로 99.99%를 차지하기 위해 커널의 크기가 급격하게 커지는 것을 볼 수 있습니다. 이렇다 보니 성능에 민감한 실시간 이미지 처리 프로그램에서는 부담스러울 수밖에 없습니다.<br /> <br /> 이런 문제점을 "<a target='tab' href='http://dev.theomader.com/gaussian-kernel-calculator/'>Gaussian Kernel Calculator</a>" 글의 코드에서 보완하고 있습니다. 이것은 주어진 sigma에 따라 커널 크기를 임의로 잡아도 전체 가중치의 합이 1이 나오도록 보정하고 있는 것입니다. (코드가 <a target='tab' href='http://dev.theomader.com/scripts/gaussian_weights.js'>자바스크립트로 공개</a>되어 있으므로 여러분들이 사용하는 언어에 맞게 포팅할 수 있습니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 어차피 보정이니, 우리도 역시 마음대로 할 수 있습니다. 어찌 되었든 중요한 것은 마스크 배열의 합을 1에 근접하도록 유지하면 되는 것입니다. 따라서, 위에서 제 코드의 경우 sigma == 1.0, kernel size == 3인 경우 커널 합이 0.88288...이었으니, 나머지 0.11712... 값에 대해 배열 전체에 고르게 나눠주는 식으로 계산해 (임의의 정밀도 ε 차이를 만족하는) 1에 가깝게 만들 수도 있을 것입니다. <br /> <br /> 종 모양의 그래프로 보면, 양쪽 0.11712... 영역의 값을 중앙에 더하므로 종 모양이 고르게 올라오는 식의 보정 결과를 갖게 됩니다.<br /> <br /> 이런 보정을 하는 코드를 좀 멋있게 작성할 수도 있겠지만, 일단 다음과 같이 간단하게 총합이 1.0이 넘기 전까지 루프로 돌려 마스크 배열 값을 구하는 식으로 해보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > double remains = (1.0 - sum) / dblArray.Length; // 커버하지 못한 종 모양의 영역 마스크 배열의 길이로 나누고, double add = remains; // 초깃값을 지정 후, double[] <span style='color: blue; font-weight: bold'>candidate</span> = dblArray; while (true) { sum = 0.0; for (int i = 0; i < dblArray.Length; i++) { dblArray[i] += remains; sum += dblArray[i]; } if (sum >= 1.0) { break; } Array.Copy(dblArray, <span style='color: blue; font-weight: bold'>candidate</span>, dblArray.Length); add += 0.000001; } </pre> <br /> 위의 코드로 sigma == 25.0, kernel size == 5x5로 했을 때 제 경우에는 다음의 커널을 얻을 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // sigma = 25.0, my float gaussian5x5BlurFilter[25] = { 0.039998,0.039998,0.039998,0.039998,0.039998, 0.039998,0.039999,0.039999,0.039999,0.039998, 0.039998,0.039999,0.039999,0.039999,0.039998, 0.039998,0.039999,0.039999,0.039999,0.039998, 0.039998,0.039998,0.039998,0.039998,0.039998, }; </pre> <br /> "<a target='tab' href='http://dev.theomader.com/gaussian-kernel-calculator/'>Gaussian Kernel Calculator</a>" 글의 경우 다음의 커널을 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // sigma = 25.0, online float gaussian5x5BlurFilter[25] = { 0.039872, 0.039968, 0.04 , 0.039968, 0.039872, 0.039968, 0.040064, 0.040096, 0.040064, 0.039968, 0.04 , 0.040096, 0.040128, 0.040096, 0.04 , 0.039968, 0.040064, 0.040096, 0.040064, 0.039968, 0.039872, 0.039968, 0.04 , 0.039968, 0.039872, }; </pre> <br /> <a target='tab' href='http://www.sysnet.pe.kr/2/0/11620'>이를 지난번 글에서 다룬 shader에 적용</a>해 보면, 지구본이 각각 다음과 같이 출력됩니다.<br /> <br /> <img alt='gaussian_kernel_1.png' src='/SysWebRes/bbs/gaussian_kernel_1.png' /><img alt='gaussian_kernel_2.png' src='/SysWebRes/bbs/gaussian_kernel_2.png' /><br /> <br /> 2개의 mask 배열에 대한 차이가 미미해서 솔직히 어느 게 어느 출력인지 구분이 안 갈 정도로 동일합니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1322&boardid=331301885'>첨부 파일은 이 글의 소스 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로 다음의 글에 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Efficient Gaussian blur with linear sampling ; <a target='tab' href='http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/'>http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/</a> </pre> <br /> 특정 크기에 대해 파스칼의 삼각형을 이용한 가우시안 분포의 근삿값을 구하는 방법을 설명하고 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2044
(왼쪽의 숫자를 입력해야 합니다.)