본문 바로가기

OpenCV없는 영상처리

기하학처리

시연 영상(유튜브) 

기하학처리 전반부 : https://youtu.be/iPlM2oqxPw4

기하학처리 후반부: https://youtu.be/kvXAoX1KfpE

<원본이미지 상하미러링 처리하기>

상하를 교체해주려면 행의 값을 거꾸로 하고 열의 값은 그대로 가면된다.

이중 for 문에서 i의 값을 뒤집고 k의 값을 그대로 해준다.

정방향으로 돌리는 이중 for문에서 행의 인덱스만 뒤집어 주려면

전체크기에서 i를 빼준다음에 전체크기가 인덱스의 최댓값보다 1크기가 크므로 (인덱스가 0~255이면 전체크기는 256이다.) 1을 빼주면 이미지의 상하를 교체해줄수 있다.

출력은 outImage만 하므로 inImage의 이미지의 상하는 변경해주지 않았다.

 

알고리즘
결과화면

 //이미지의 상하를 반전시키는 함수
            function udImage(){
                //원본이미지의 크기는 변하지 않는다.
                outW=inW;
                outH=inH;
                //출력할 배열 생성(출력이미지)
                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++){
                        //inW는 배열의 크기 자체이므로 -1해야함
                        outImage[i][k]=inImage[inH-i-1][k];
                    }
                }
                //출력함수
                displayImage();
            }

 

 

<원본 이미지 좌우미러링 처리하기>

좌우를 교체해주려면 이중 for문에서 i는 가만히 있고 k만 변경하면 된다.

열의 인덱스를 뒤집어 주려면 

전체 크기에서 k를 빼준다음에 전체크기가 인덱스의 최댓값보다 1크기 크므로 1을빼주면 이미지의 좌우를 교체해줄 수있다.

출력은 outImage가 하므로 원본이미지 inImage의 좌우는 변경하지 않고 인덱스만 바꾸어서 outImage로 대입해준다.

알고리즘
결과화면

//이미지의 좌우를 반전시키는 함수
            function lrImage(){
                //원본이미지의 크기는 변하지 않는다.
                outW=inW;
                outH=inH;
                //출력할 배열 생성(출력이미지)
                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++){
                        //inW는 배열의 크기 자체이므로 -1해야함
                        outImage[i][k]=inImage[i][inW-k-1];
                    }
                }
                //출력함수
                displayImage();
            }

 

 

<원본이미지 영상이동 처리하기>

영상의 x축은 열이고 y축은 행이다.

배열의 열은 2번째 인덱스이고 행은 1번째 인덱스이다.

x축과 y축의 값을 입력받아 x는 2번째 인덱스에 y는 1번째 인덱스 값에 더해줘서 이미지를 이동시킨다.

영상을 이동한 후 캔버스 밖의 부분은 잘라내기 위해 if 문으로 이동한 값이 출력이미지의 크기를 넘는다면 출력하지 않도록 설정한다.

break;를 써준이유는 이미지가 캔버스 밖으로 나가게된 열의 첫번째 값부터 끝까지는 캔버스밖을 완벽히 벗어나므로 반복문을 멈추고 다음행을 출력한다.(break; 한개는 하나의 반복문을 탈출한다.)

그리고 이미지 밖의 첫번째행은 시작부터 캔버스 밖을 벗어나므로 그 행의 출력을 시작도 하지 않고 생략!생략!생략!해서

for문의 반복을 끝낸다.

알고리즘
결과화면

 //이미지를 입력받은 값만큼 이동
            function swapImage(){
                //이미지를 이동시킬 x값과 y값을 입력받는다.
                let x=parseInt(prompt("x축으로 이동","30"));
                let y=parseInt(prompt("y축으로 이동","30"));
                //원본이미지의 크기는 그대로 간다
                //이동시키고 캔버스밖의 부분은 잘린상태로 나온다.
                outW=inW;
                outH=inW;
                //출력할 배열생성
                outImage=new Array(outH);
                for(let i=0;i<outH;i++)
                    outImage[i]=new Array(outW);
                //이미지 이동시키는 for문
                for(i=0;i<outH;i++){
                    for(k=0;k<outW;k++){
                        //이동은 인덱스에 x와 y의 좌표를 더해서 출력배열에 대입하는 형태로한다.
                        //원본이미지 크기 그대로 가므로 만약 인덱스에 x,y좌표를 더한 값이
                        //원본이미지보다 크게 되면 출력배열에 대입하는 것을 for문을멈춘다.(나머지부분을 잘라냄)
                        if((i+y)<outH&&(k+x)<outW)
                            outImage[i+y][k+x]=inImage[i][k];
                        else break;
                    }
                }
                //출력함수
                displayImage();
            }

 

 

 

 

<원본 이미지 영상회전 처리하기>

이미지 회전은 아래 그림에 적혀있는 공식으로 영상회전을 끝내고 나면 이 이미지가 좌표평면의 0.0을 기준으로 회전하므로 회전후에 이미지는 캔버스 밖으로 이동해져 있다. 이 이미지가 회전으로 인해 옮겨간 값을 다시 x와 y값에 더해주므로써 해결한다.

 

알고리즘
결과화면

            //이미지를 회전시키는 함수
            function rotateImage(){
                //회전할 각의 크기를 입력받아 degree에 저장한다.
                let degree = parseFloat(prompt("회전각도를 입력하시오","45"));
                //각도를 라디안으로 변경
                let radian = degree*Math.PI/180.0;
                //이미지를 돌리는건 행렬 공식에 의한다
                //45도 돌리게 되면 루트2를 곱해주고 그래야하는데 그걸 각도별로 쓸순 없으니까
                //출력이미지크기가 이렇게 된다
                //절댓값을 씌우면 90도가 넘게 돌아간다.
                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;
                    }
                }
                //xd,yd는 이미지를 이동시킬 목적지 좌표(인덱스)이다.
                let xd, yd;
                //cx,cy는 이미지의 중심 좌표값이다.
                //이미지가 한 부분을 고정으로 돌아가야 하므로 설정하는데
                //논리상 출력이미지의 크기의 절반값을 하면 출력이미지의 중심좌표 값이
                //나와서 그걸 기준으로 돌아가야하는데
                //자꾸 이미지가 캔버스를 탈출한다....이미지가 굴러가서 사라져버린다.
                //해결-->오타였다.....cy/outW*inW를 cy/outW+inW로 썼다.
                let cx=parseInt(outH/2);
                let cy=parseInt(outW/2);
                //이젠 목적지 인덱스를 활용해서 출력 이미지에 원본이미지를 대입해줄차례
                for(let i=0;i<outH;i++){
                    for(let k=0;k<outW;k++){
                        //이것 역시 행렬이다 
                        //cx/outH*inH와 cy/outW*inW는 왜 나온지 모르겠다.
                        //이미지가 중심점을 따라 회전하기 때문에 이미지가 캔버스밖으로 이동한다.
                        //따라서 회전으로 인해 옮겨간 만큼 다시 더해주어야한다.
                        xd=parseInt(Math.cos(radian)*(i-cx)-Math.sin(radian)*(k-cy)+cx/outH*inH);
                        yd=parseInt(Math.sin(radian)*(i-cx) + Math.cos(radian)*(k-cy)+cy/outW*inW);
                        //목적지 인덱스가 원본이미지의 인덱스 내에 있는 수라면
                        //출력 배열에 대입한다.
                        if((0<=xd && xd <inH) && (0<=yd && yd <inW))
                            outImage[i][k]=inImage[xd][yd];
                        //목적지 인덱스가 원본이미지 인덱스 밖이라면 0대입
                        else
                            outImage[i][k]=255;
                    }
                }
                //출력함수
                displayImage()
            }

 

 

<원본이미지 영상회전(90도) 처리하기>

이미지를 90도 회전하도록 만들려면 이미지를 4면으로 쪼개서 가장 바깥쪽 줄 부터 한 줄씩 교체해 나간다.

1면이 삼각형이므로 for문이 반복 될수록 교체되는 픽셀의 수는 점차 감소해나간다.

 

 

알고리즘
결과화면

 //이미지를 90도씩 회전하는 함수
            function image90(){
                //90도씩 회전하므로 출력크기는 변하지 않는다
                outH=inH;
                outW=inW;
                //출력할 배열생성
                outImage=new Array(outH);
                for(let i=0;i<outW;i++)
                    outImage[i]=new Array(outW);
                //색종이를 삼각형으로 1/4크기로 접어서 나온 4조각에 2차원 배열이 위에
                //얹어진 모양을 생각하면서 한 조각에서 한줄씩 옮긴다고 생각하고 작성한 for문이다.
                let temp;
                for(let i=0;i<inH/2;i++){
                    for(let k=i;k<inW-i;k++){
                        //4개의 꼭짓점에서 하나씩 옮기는 부분으로
                        //3x3배열을 그려놓고 그대로인 인덱스는 i로 설정하고, 변하는 부분은 k로 설정하면서
                        //역순과 정방향을 생각하면 배열의 위치를 바꿀수 있다.
                        temp=inImage[i][k];
                        inImage[i][k]=inImage[inH-k-1][i];
                        inImage[inH-k-1][i]=inImage[inW-i-1][inH-k-1];
                        inImage[inW-i-1][inH-k-1]=inImage[k][inW-i-1];
                        inImage[k][inW-i-1]=temp;
                    }
                }
                //바뀐 원본이미지 출력을 위해 출력배열에 대입한다.
                for(let i=0;i<inH;i++){
                    for(let k=0;k<inW;k++){
                        outImage[i][k]=inImage[i][k];
                    }
                }
                //출력함수
                displayImage();
            }

 

 

<원본 이미지 영상축소 처리하기>

영상을 축소할때 원본이미지만큼 for문을 돌리고 축소 이미지의 인덱스는 원본이미지 값의 1/2로 한다.

알고리즘
결과화면

//이미지를 입력받은 값만큼 축소하는 함수
            function zoomOutImage(){
                //이미지를 몇배 축소 할 것 인지 입력받는다
                let scale = parseInt(prompt("축소 크기 : ","2"));
                //출력이미지의 크기가 1/scale만큼 줄어든다.
                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문을 돌린다.
                for(let i=0;i<inH;i++){
                    for(let k=0;k<inW;k++){
                        //출력이미지의 크기는 원본이미지보다 1/scale 곱한것 만큼 작으므로
                        //인덱스에 1/scale해서 곱해준다.
                        outImage[parseInt(i/scale)][parseInt(k/scale)]=inImage[i][k];
                    }
                }
                //출력함수
                displayImage();
            }

 

 

<원본이미지 영상확대 처리하기>

알고리즘
결과화면

 //이미지를 입력받은 값 만큼 확대하는 함수
            function zoomInImage(){
                //이미지를 몇배 확대할 것인지 입력 받는다.
                let scale= parseInt(prompt("확대 크기: ","2"));
                //출력이미지의 크기가 scale만큼 커진다.
                outH=inH*scale;
                outW=inW*scale;
                //출력할 배열 생성
                outImage= new Array(outH);
                for(let i=0;i<outH;i++)
                    outImage[i]=new Array(outW);
                //원본이미지의 크기를 확대하므로 원본이미지 크기만큼만 for문을 돌린다.
                for(let i=0;i<inH;i++){
                    for(let k=0;k<inW;k++){
                        //출력이미지가 원본보다 scale배한만큼 크기 때문에
                        //인덱스에 scale을 곱해준다.
                        //출력이미지가 훨씬 커서 빈 인덱스가 생겨 출력 이미지가 어두워진다.
                        //해결방법 : 백워딩
                        outImage[i*scale][k*scale]=inImage[i][k];
                    }
                }
                //출력함수
                displayImage();
            }

 

 

<원본 이미지 영상확대(백워딩) 처리하기>

알고리즘
결과화면

//이미지를 입력받은 값만큼 확대하는 함수 , 백워딩
            function zoomInImage2(){
                //이미지를 몇배 확대할 것인지 입력받는다.
                let scale=parseInt(prompt("확대 크기: ","2"));
                //출력이미지의 크기가 scale만큼 커진다.
                outH=inH*scale;
                outW=inW*scale;
                //출력할 배열 생성
                outImage= new Array(outH);
                for(let i=0;i<outH;i++)
                    outImage[i]=new Array(outW);
                //출력이미지의 인덱스를 활용해서 원본이미지의 픽셀을 쪼개 넣을 것이므로
                //출력이미지의 크기만큼 for문을 돌린다.
                for(let i=0;i<outH;i++){
                    for(let k=0;k<outW;k++){
                        //출력이미지에 원본이미지를 넣는다.
                        //출력이미지의 인덱스를 1/scale해서 원본이미지의 하나의 픽셀을 
                        //출력이미지의 한 영역에 중복해서 넣는다 4x4,2x2로 그림그리면 알게됨
                        outImage[i][k]= inImage[parseInt(i/scale)][parseInt(k/scale)];
                    }
                }
                //출력함수
                displayImage();
            }

 

 

'OpenCV없는 영상처리' 카테고리의 다른 글

영상처리의 전처리부  (0) 2022.09.12
히스토그램 처리  (0) 2022.09.11
화소영역처리  (0) 2022.09.11
화소 점 처리  (0) 2022.09.11
인공지능과 영상처리  (0) 2022.09.11