영상 처리 프로그래밍 - 기하학 처리

이효정·2022년 9월 19일

기하학 처리
영상을 구성하는 화소의 공간적 위치를 재배치하는 과정
ex) 상하 미러링, 좌우 미러링, 축소하기, 확대하기, 이동하기, 회전하기

<상하 미러링>
-상하를 담당하는 열의 값을 최댓값에서 빼면 상하 미러링 처리된다

temp = inImage[i][k]
inImage[i][k] = inImage[inH-i-1][k]
inImage[inH-i-1][k] = temp

function upDownImage() { // 영상 상하미러링 알고리즘
            outH = inH;
            outW = inW;
            // 출력 영상의 2차원 메모리 할당
            outImage = new Array(outH);
            for(let i=0; i<outH; i++)
                outImage[i] = new Array(outW);
            // **** 진짜 영상처리 알고리즘 *****
            let temp;
            for (let i = 0; i < inH/2; i++) {
                for (let k = 0; k < inW; k++) {
                temp = inImage[i][k];
                inImage[i][k] = inImage[inH-i-1][k];
                inImage[inH-i-1][k] = temp;
                    }
                }
            for (let i = 0; i < inH; i++) {
                for (let k = 0; k < inW; k++) {
                outImage[i][k] = inImage[i][k];
                }
            }
            // ******************************
            displayImage();
        }

<좌우 미러링>
-좌우를 담당하는 행의 값을 최댓값에서 빼면 좌우 미러링 처리된다

temp = inImage[i][k]
inImage[i][k] = inImage[i][inW-k-1]
inImage[i][inW-k-1] = temp

function leftRightImage() { // 영상 좌우미러링 알고리즘
            outH = inH;
            outW = inW;
            outImage = new Array(outH);
            for(let i=0; i<outH; i++)
                outImage[i] = new Array(outW);
            // **** 진짜 영상처리 알고리즘 *****
            let temp;
            for (let i = 0; i < inH; i++) {
                for (let k = 0; k < inW / 2; k++) {
                temp = inImage[i][k];
                inImage[i][k] = inImage[i][inW-k-1];
                inImage[i][inW-k-1] = temp;
                    }
                }
            for (let i = 0; i < inH; i++) {
                for (let k = 0; k < inW; k++) {
                outImage[i][k] = inImage[i][k];
                }
            }
                    
            // ******************************
            displayImage();
        }

<축소하기(÷2)>
-원본 이미지의 행과 열을 축소 배율로 나누어 출력

outImage[parseInt(i/scale)][parseInt(k/scale)]= inImage[i][k])

function zoomOutImage() { //  축소 알고리즘
            let scale = parseInt(prompt("배율","2"));
            outH = parseInt(inH/scale);
            outW = parseInt(inW/scale);
            outImage = new Array(outH);
            for(let i=0; i<outH; i++)
                outImage[i] = new Array(outW);
            // **** 진짜 영상처리 알고리즘 *****
            for (let i=0; i<inH; i++) {
                for (let k=0; k<inW; k++) {
                    outImage[parseInt(i/scale)][parseInt(k/scale)]= inImage[i][k];
                }
            }
            // ******************************
            displayImage();
        }

<확대하기(×2)>
-입력 이미지의 화소는 출력 이미지의 행과 열을 입력한 배율만큼 나눈 값으로 지정
-커진 출력 이미지 공간에 이웃 화소를 채워 넣는 방식

outImage[i][k]= inImage[parseInt(i/scale)][parseInt(k/scale)]

function zoomInImage() { //  확대 알고리즘. 백워딩(홀이 안 생기도록)
            let scale = parseInt(prompt("배율","2"));
            outH = parseInt(inH*scale);
            outW = parseInt(inW*scale);
            outImage = new Array(outH);
            for(let i=0; i<outH; i++)
                outImage[i] = new Array(outW);
            // **** 진짜 영상처리 알고리즘 *****
            for (let i=0; i<outH; i++) {
                for (let k=0; k<outW; k++) {
                    outImage[i][k]= inImage[parseInt(i/scale)][parseInt(k/scale)];
                }
            }
            // ******************************
            displayImage();
        }

<이동하기(50,50)>
-출력 이미지 값에 입력값을 더하여 이미지를 좌표상에서 이동

if (i+y < outH && k+x < outW)
outImage[i+y][k+x] = inImage[i][k];
else outImage[i][k] === undefined

function moveImage() {        // 영상 이동 알고리즘
            outH = inH;
            outW = inW;
            outImage = new Array(outH);
            for(let i=0; i<outH; i++)
                outImage[i] = new Array(outW);
           // **** 진짜 영상처리 알고리즘 *****
            let x = parseInt(prompt("x축 이동", "50"));
            let y = parseInt(prompt("y축 이동", "50"));
            for (let i=0; i<outH; i++) {
            for (let k=0; k<outW; k++) {
                if (i+y < outH && k+x < outW)
                outImage[i+y][k+x] = inImage[i][k];
                    else outImage[i][k] === undefined;
                }   
            }
           // ******************************
           displayImage();
       }

<회전하기(캔버스 크기 유지)>
-radian으로 변경하여 사용

radian = angle * Math.PI / 180.0
cx = outH / 2
cy = outW / 2

yin = parseInt(Math.cos(radian)*(k-cx) + Math.sin(radian)*(i-cy)  + cx )
xin = parseInt(-Math.sin(radian)*(k-cx) + Math.cos(radian)*(i-cy)  + cy )

outImage[i][k] = inImage[xin][yin]
![](https://velog.velcdn.com/images/ihj0518518/post/851e592a-696c-43fd-8510-f5580435d221/image.png)

function rotate1Image() { // 회전 알고리즘
            // (중요!) 출력 이미지의 크기가 결정 ---> 알고리즘에 의존...
            outH = inH;
            outW = inW;
            // 출력 영상의 2차원 메모리 할당
            outImage = new Array(outH);
            for(let i=0; i<outH; i++)
                outImage[i] = new Array(outW);

            // 초기화 (255)
            for (let i=0; i<outH; i++) 
                for (let k=0; k<outW; k++) 
                    outImage[i][k] = 255;

            // **** 진짜 영상처리 알고리즘 *****
            let degree = parseInt(prompt("각도","45"));
            let radian = degree * Math.PI / 180.0;

            let cx = outH / 2;
            let cy = outW / 2;
            // xout = cos*xin - sin*yin
            // yout = sin*xin + cos*yin
            for (let i=0; i<outH; i++) {
                for (let k=0; k<outW; k++) {
                    let yin = parseInt(Math.cos(radian)*(k-cx) + Math.sin(radian)*(i-cy)  + cx );
                    let xin = parseInt(-Math.sin(radian)*(k-cx) + Math.cos(radian)*(i-cy)  + cy );
                    if ((0 <= xin && xin < inH )&& (0 <= yin && yin < inW ))
                        outImage[i][k] = inImage[xin][yin];
                }
            }
            // ******************************
            displayImage();
        }

<회전하기(원본 크기 유지)>
-출력 이미지의 회전 후 크기를 미리 확인하여 목적지의 중앙에서 회전 시키는 방식

radian = degree * Math.PI / 180.0
cx = outH / 2
cy = outW / 2

yin = parseInt(Math.sin(radian)*(i-cx) + Math.cos(radian)*(k-cy)+cy/outW*inW)
xin = parseInt(Math.cos(radian)*(i-cx)-Math.sin(radian)*(k-cy)+cx/outH*inH)

outImage[i][k] = inImage[xin][yin]

function rotate2Image() {      // 영상 회전 알고리즘
         let degree = parseFloat(prompt("각도","45"));
         //회전할 각의 크기를 입력받아 degree에 저장한다.
         //각도를 라디안으로 변경
                let radian = degree*Math.PI/180.0;
                outH=parseInt(Math.abs(Math.cos(radian)*inH) + Math.abs(Math.cos(Math.PI/2-radian)*inW));
                outW= parseInt(Math.abs(Math.cos(radian)*inW) + Math.abs(Math.cos(Math.PI/2-radian)*inH));
                //출력할 배열 생성
                outImage=new Array(outH);
                for(let i=0;i<outH;i++)
                    outImage[i]=new Array(outW);
                //출력 배열을 0으로 초기화
                for(let i=0;i<outH;i++){
                    for(let k=0;k<outW;k++){
                        outImage[i][k]=255;
                    }
                }
                 //xin,yin는 이미지를 이동시킬 목적지 좌표(인덱스)이다.
                let xin, yin;
                //cx,cy는 이미지의 중심 좌표값이다.
                //이미지가 한 부분을 고정으로 돌아감
                let cx=parseInt(outH/2);
                let cy=parseInt(outW/2);
                //목적지 인덱스를 활용해서 출력 이미지에 원본이미지를 대입
                for(let i=0;i<outH;i++){
                    for(let k=0;k<outW;k++){
                        //이것 역시 삼각함수의 2배각 공식
                        xin=parseInt(Math.cos(radian)*(i-cx)-Math.sin(radian)*(k-cy)+cx/outH*inH);
                        yin=parseInt(Math.sin(radian)*(i-cx) + Math.cos(radian)*(k-cy)+cy/outW*inW);
                        //목적지 인덱스가 원본이미지의 인덱스 내에 있는 수라면
                        //출력 배열에 대입
                        if((0<=xin && xin <inH) && (0<=yin && yin <inW))
                            outImage[i][k]=inImage[xin][yin];
                    }
                }
            // ******************************
            displayImage();
            }

0개의 댓글