본문 바로가기
대한상공회의소 스마트팩토리 교육/C# 프로그래밍

[시각화 프로그래밍] Color Image Processing Using C# «수업-5» : 마우스 이벤트 - 사각형 선택 영역만 이미지 영상처리 수행

by 나는영하 2022. 4. 14.

※ 주의사항 

본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!

감사합니다😁

 

안녕하세요!!

오늘은 아래와 같은 내용을 확인해보겠습니다.

 

마우스 이벤트

MouseUp

MouseDown


 

지난시간까지 회색조(Gray Scale) 이미지 영상처리 프로그램을 만들었다면 이번부터는 칼라 이미지를 처리하는 프로그램을 만들어 보았습니다. 칼라라고 해서 엄청 어렵고 완전 다르진 않았습니다!! 제일 중요한 개념은 화소점 값을 저장하는 배열이 1개에서 3개가 된다는 점입니다. 따라서 그에대한 화소점 및 화소영역 처리 과정을 1번에서 3번 반복하게 됩니다.(대부분 for문을 사용해서 하겠죠??) 자세한 내용은 추후에 프로젝트가 마무리되면 정리하도록 하겠습니다. 

 

오늘 확인할 내용은 마우스 클릭을 통해 이미지의 영역을 설정하고 해당 부분만 이미지 영상처리를 수행하는 방법입니다. 사용자가 어떤 특정한 행동을 할때만 어떠한 사건이 발생하는것을 '이벤트'라고 합니다. 마우스 영역처리를 구현하기 위해서는 이벤트 중에서도 '마우스 이벤트'를 사용해서 구현해야합니다. 

 

1. 마우스 이벤트 사용 기본 및 종류 

PictureBox의 속성창 & 이벤트창

먼저 이벤트를 사용하기 위해서는 메인폼의 디자인탭에서 특정한 항목의 속성창에 들어가야 합니다.

위의 그림과 같이 속성창에 있는 번개모양의 아이콘을 누르면 해당 항목의 이벤트를 설정할 수 있습니다. 

C#의 친절함...

여러 이벤트중에서 하나를 클릭하면 하단부에 해당 이벤트에 대한 설명이 적혀있습니다.

 

C#에서 이벤트의 종류는 엄청 많으며 이를 외우는 것은 당연 불가능합니다. 

하지만 이벤트의 이름을 보면 어느정도 대충 유추가 가능하니 이름만 보고 코딩을 할 수 있는 습관을 기르는것이 중요할 것 같습니다. (그리고 주로 사용하는 이벤트가 정해져 있어 이벤트의 숫자만 보고 겁먹을 필요는 없을것 같습니다!!)😁

마우스 이벤트 종류

오늘 저는 마우스 이벤트만 사용할테니 마우스 이벤트의 종류에 대해서만 간단히 알고 넘어가겠습니다.

 

※ 마우스 이벤트 종류

① MouseDown : 마우스 포인터가 구성 요소 위에 있을 때 마우스 단추를 누르면 발생

② MouseEnter : 마우스를 컨트롤의 보이는 부분으로 가져갈 때 발생

③ MouseHover : 마우스가 일정 시간 동안 컨트롤의 내부에 고정되어 있을 때 발생

④ MouseLeave : 마우스가 컨트롤의 보이는 부분에서 벗어날 때 발생

⑤ MouseMove : 마우스 포인터를 구성 요소 위로 이동할 때 발생

⑥ MouseUp : 마우스 포인터가 구성 요소 위에 있을 때 마우스 단추를 놓으면 발생

여기서 컨트롤이란 PictureBox를 의미하며 이는 이벤트를 발생시키는 항목(?), 요소(?)에 따라 달라집니다.

 

저는 이번 프로젝트(Color Image Processing)에서 MouseDown, MouseMove, MouseUp 이렇게 3개의 마우스 이벤트만 사용하였습니다. 

MouseDown은 마우스를 클릭하면 마우스 클릭 버튼이 내려가니 Down이라 생각하면되고,

MouseUp은 마우스를 클릭하고 놓으면 버튼이 내려갔다 올라가니 Up이라 생각하면 쉽습니다! 

 

MouseEnter를 더블클릭 하면 관련 함수가 생성된다.

사용하고 싶은 이벤트를 항목에서 더블클릭을 하면 해당코드가 생성되며 거기서 코딩을 하면 됩니다!!


2. 선택 영역만 이미지 영상처리 수행

2-1. 기본 개념 및 코딩 과정

마우스가 이미지의 위에서 범위를 지정할때만 이벤트가 발생하면 되기 때문에 PictureBox내의 마우스 이벤트만 사용합니다. 메인폼이나 다른 요소에서 마우스 이벤트를 지정하면 안되니 이점 유의해주세요!

 

코딩 과정은 간단합니다.

① PictureBox의 MouseDown 이벤트 생성

마우스를 클릭하면 클릭한 지점의 마우스 좌표를 특정 변수(sx, sy)에 저장합니다. 

 

② PictureBox의 MouseUp 이벤트 생성

마우스를 드래그 후 왼쪽 버튼을 놓으면 마우스를 놓은 지점의 좌표를 특정 변수(ex, ey)에 저장합니다.

마우스 드래그 이벤트는 '선택영역 이미지처리'하는 과정에선 사용하지 않지만 나중에 러버밴드(Rubber Band)를 구현할때 필요하니 미리 이벤트를 생성해두었습니다.  

 

③ 이미지 영상처리 수행

두 지점의 좌표를 가지고 이미지 영상처리를 수행할 범위를 구하고, 해당 범위만 영상처리 알고리즘을 적용시키고 범위를 벗어나면 원래의 화소점 값을 그대로 출력합니다. 


2-2. 전역 변수

        int sx = -1, sy = -1, ex = -1, ey = -1;
        char processType = '0'; // 영상처리 종류 
        string mouseStatus = "None";

두개의 지점의 좌표값을 지정할 변수를 선언하고 초기값을 음수로 설정합니다.

음수로 설정하는 이유는 좌표값에서 음수는 없기 때문입니다.(0으로 하면 0,0으로 인식할 수 있다)

 

processType 변수의 값에 따라 어떠한 이미지 영상처리를 수행할지 결정합니다.

우측 상단의 ComboBox를 이용해서 마우스 사용 여부 선택

mouseStatus 변수에 따라 마우스 영역을 설정해서 이미지 영상처리를 할지, 이미지 전체를 영상처리할지 선택합니다. mouseStatus 변수의 값이 "None" 이면 이미지 전체를 영상처리하고 "Box"면 마우스 이벤트를 발생시켜서 선택한 영역만 이미지 영상처리를 수행합니다.

 

 


2-3. toolStripComboBox_TextChanged 이벤트 함수부 

	private void toolStripComboBox1_TextChanged(object sender, EventArgs e)
        {
            mouseStatus = toolStripComboBox1.Text;
            if (toolStripComboBox1.Text == "None") // NONE이면 전체 영상 효과 주게 편집
            {
                processType = '0';
            }
            if (toolStripComboBox1.Text == "Box")
            {
                menuStrip1.Items[2].Enabled = false;
            }
            else menuStrip1.Items[2].Enabled = true;
        }

toolStripComboBox의 항목이 변경될때마다 이벤트를 발생시킵니다. (TextChanged) 

ComboBox의 Text값을 mouseStatus 변수에 저장합니다.

Text 값이 "None"이면 processType에 0을 저장해서 이미지 전체를 영상처리 합니다.

Text 값이 "Box"이면 기하학 처리 메뉴 버튼을 비활성화 합니다.

(기하학 처리는 이미지 부분 영상처리를 적용하지 않았습니다. 활용도가 미비하기 때문에...)


2-4. MouseDown 이벤트 함수부

 	private void PB_OutImage_MouseDown(object sender, MouseEventArgs e)
        {
            if (processType == '0') return;
            sx = e.X;
            sy = e.Y;
            mouseDown = true;
        }

PictureBox(PB_OutImage)위에 마우스를 클릭하면 이벤트가 발생합니다.

만약 processType이 0이면(ComboBox의 값을 None으로 설정된 상태) 리턴됩니다.  

0이 아니라면 마우스를 클릭한 지점의 좌표를 각 변수(sx, sy)에 저장합니다.

mouseDown 변수는 러버 밴드에 사용되는 변수이므로 설명은 생략합니다.

 


2-5. MouseUp 이벤트 함수부

	 private void PB_OutImage_MouseUp(object sender, MouseEventArgs e)
        {
            if (processType == '0') return;
            ex = e.X;
            ey = e.Y;

            // 시작점 , 끝점 재배열
            if (sx > ex)
            {
                int tmp = sx;
                sx = ex;
                ex = tmp;
            }
            if (sy > ey)
            {
                int tmp = sy;
                sy = ey;
                ey = tmp;
            }
            
            switch (processType)
            {
                case 'A': reversImage(); break;
                case 'B': brightDarkImage(); break;
                case 'C': sharpDimImage(); break;
                /* ///////////// 코드 생략 /////////////
                processType 값에 따라 각기 다른 영상처리 효과가 수행됨 */
            }
            // processType = '0'; // 한번만이 아니라 다른메뉴를 누르기전까지 계속 범위 지정 가능
        }

PictureBox(PB_OutImage)위에 마우스 클릭을 놓으면 이벤트가 발생합니다.

만약 processType이 0이면(ComboBox의 값을 None으로 설정된 상태) 리턴됩니다.  

0이 아니라면 마우스 클릭을 놓은 지점의 좌표를 각 변수(ex, ey)에 저장합니다.

 

만약 마우스를 클릭한 지점의 좌표와 클릭을 놓은 지점의 좌표값의 크기가 반대인경우 각 변수에 저장된 값을 바꿔줍니다. (int tmp 변수 사용)

 

해당 코드 마지막줄의 processType = '0'을 주석처리 안하면 범위 한번을 지정하고 나면 다시 메뉴를 선택해야 하는 번거로움이 발생합니다. 


2-6. 이미지 영상처리 함수부

많은 영상처리 기능중 반전 처리만 예시로 확인해보겠습니다.

 

(1) 메뉴 클릭 이벤트 함수

 	private void 반전ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mouseStatus == "None")
            {
                reversImage();
            }
            else
            {
                processType = 'A';
            }

        }

- ComboBox의 값이 "None"이면 이미지 전체를 영상처리하고 그 외의 경우라면 마우스로 선택한 영역만 이미지 영상처리를 수행합니다. ( processType = 'A' )

 

(2)  이미지 영상처리 함수부

 	void reversImage()
        {
            if (inImage == null) return;
            // 중요! 출력 영상의 크기 --> 알고리즘에 영향  
            outH = inH; outW = inW;
            // 출력 메모리 확보
            outImage = new byte[RGB, outH, outW];
            // **** 진짜 영상처리 알고리즘 ****
            if (processType == '0')
            {
                sx = 0; ex = inH;
                sy = 0; ey = inW;
            }
            for (int rgb = 0; rgb < RGB; rgb++)
            {
                for (int i = 0; i < inH; i++)
                {
                    for (int k = 0; k < inW; k++)
                    {
                        if ((sx <= i && i <= ex) && (sy <= k && k <= ey))
                        {
                            outImage[rgb, i, k] = (byte)(255 - inImage[rgb, i, k]);
                        }
                        else
                        {
                            outImage[RR, i, k] = inImage[RR, i, k];
                            outImage[GG, i, k] = inImage[GG, i, k];
                            outImage[BB, i, k] = inImage[BB, i, k];
                        }
                    }
                }
            }
            displayImage();
        }

ComboBox의 값이 "None"이거나 초기상태이면 이미지 전체를 영상처리하기위해 sx, sy, ex, ey의 좌표 값을 이미지의 배열의 시작과 끝점으로 설정합니다. 

 

화소점값을 outImage[,,,]에 저장하는 3중 for문 내에 if문을 사용해서 마우스로 설정한 범위내는 reverse 처리를 수행하고 설정한 범위를 넘어가는 경우는 inImage의 값은 그대로 outImge에 저장해서 출력합니다. 

 

 

댓글