ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android NDK + OpenCV 관심영역(ROI)에 영상처리하는 예제
    OpenCV/Android 개발 환경 및 예제 2019. 5. 13. 19:08

     

    카메라로부터 캡처된 영상에  관심영역(ROI)을 지정하여 영상처리 하는 예제입니다.  

    위치는 텍스트로된 코드에서 확인하고 코드 복사는 코드블록에 있는 것을 사용하세요. 



    업데이트

    2019. 5. 11

     

     

    유튜브 영상 또는 아래 글을 보고 진행하세요.

     

     

     

    아래 포스팅 진행한 후 필요한 부분을 추가해서 완성합니다.

     

    Android NDK + OpenCV 카메라 예제 및 프로젝트 생성방법(CMake 사용)

    https://webnautes.tistory.com/1054




    추가 1 : MainActivity.java

     

    public class MainActivity extends AppCompatActivity

           implements CameraBridgeViewBase.CvCameraViewListener2, View.OnTouchListener {

    public class MainActivity extends AppCompatActivity
    
           implements CameraBridgeViewBase.CvCameraViewListener2, View.OnTouchListener {




    추가 2 : MainActivity.java

     

       private Mat matInput;

       private Mat matResult;

       private Mat matInput1, matInput2;

       private Mat matMask=null, matNotMask=null;

       Rect rect = new Rect();

       private int step = -1;

       private Mat matInput1, matInput2;
       private Mat matMask=null, matNotMask=null;
       Rect rect = new Rect();
       private int step = -1;



    추가 3 : MainActivity.java

     

       @Override

       protected void onCreate(Bundle savedInstanceState) {

           . . . . . . . . . . . . . .  . . . . .

           mOpenCvCameraView.setOnTouchListener(this);

    mOpenCvCameraView.setOnTouchListener(this);



    추가 4 : MainActivity.java

     

       @Override

       public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {

     

           matInput = inputFrame.rgba();



           if ( matResult == null )

               matResult = new Mat(matInput.rows(), matInput.cols(), matInput.type());

     

           if ( matInput1 == null )

               matInput1 = new Mat(matInput.rows(), matInput.cols(), matInput.type());

     

           if ( matInput2 == null )

               matInput2 = new Mat(matInput.rows(), matInput.cols(), matInput.type());

     

           if ( matNotMask == null )

               matNotMask = new Mat(matInput.rows(), matInput.cols(), matInput.type());



           if ( step == 1){

               Imgproc.circle(matInput, new Point(rect.x, rect.y),  20, new Scalar(0, 255, 0, 255), -1);

               return matInput;

     

           }else if (step == 2){

     

               if (matMask != null ) {

                   Core.bitwise_and(matInput, matMask, matInput1);

                   ConvertRGBtoGray(matInput1.getNativeObjAddr(), matInput1.getNativeObjAddr());

     

                   if ( matMask == null || matNotMask == null ){

                       return matInput;

                   }

     

                   Core.bitwise_not(matMask, matNotMask);

                   Core.bitwise_and(matInput, matNotMask, matInput2);

     

                   Imgproc.cvtColor(matInput1, matInput1, Imgproc.COLOR_GRAY2RGBA);

     

                   Core.bitwise_or(matInput1, matInput2, matResult);

     

                   return matResult;

               }

     

           }

     

           return matInput;

       }

           if ( matInput1 == null )
               matInput1 = new Mat(matInput.rows(), matInput.cols(), matInput.type());
    
           if ( matInput2 == null )
               matInput2 = new Mat(matInput.rows(), matInput.cols(), matInput.type());
    
           if ( matNotMask == null )
               matNotMask = new Mat(matInput.rows(), matInput.cols(), matInput.type());
    
    
           if ( step == 1){
    
               Imgproc.circle(matInput, new Point(rect.x, rect.y),  20, new Scalar(0, 255, 0, 255), -1);
               return matInput;
    
           }else if (step == 2){
    
               if (matMask != null ) {
                   Core.bitwise_and(matInput, matMask, matInput1);
                   ConvertRGBtoGray(matInput1.getNativeObjAddr(), matInput1.getNativeObjAddr());
    
                   if ( matMask == null || matNotMask == null ){
                       return matInput;
                   }
    
                   Core.bitwise_not(matMask, matNotMask);
                   Core.bitwise_and(matInput, matNotMask, matInput2);
                   Imgproc.cvtColor(matInput1, matInput1, Imgproc.COLOR_GRAY2RGBA);
                   Core.bitwise_or(matInput1, matInput2, matResult);
    
                   return matResult;
               }
    
           }
    
           return matInput;
    
       }




    추가 5 : MainActivity.java

     

         @Override

       public boolean onTouch (View v, MotionEvent event){



           int width = matInput.cols();

           int height = matInput.rows();



           // 카메라 뷰와 이미지 좌표 맞추기

           int xOffset = (mOpenCvCameraView.getWidth() - width) / 2;

           int yOffset = (mOpenCvCameraView.getHeight() - height) / 2;

     

           int x = (int) event.getX() - xOffset;

           int y = (int) event.getY() - yOffset;



           if (x < 0 || y < 0 || x >= width || y >= height) {// 터치가 범위 벗어난 경우

     

               step = -1;

               rect.x = rect.y = rect.width = rect.height = 0;

               matMask = null;

               return false;

           }



           if ( step == 2) { // ROI 설정된 상태에서 또 터치하면 설정 취소

               step = -1;

               rect.x = rect.y = rect.width = rect.height = 0;

               matMask = null;

               return false;

           }



           if ((step == -1)&&((rect.x == 0 && rect.y == 0) || (rect.width != 0 && rect.height != 0))) { //첫번째 클릭

     

               step = 1;

               matMask = null;

               rect.x = x;

               rect.y = y;

               rect.width = rect.height = 0;

     

           } else if ( step == 1){  //두번째 클릭

     

               rect.width = x - rect.x;

               rect.height = y - rect.y;

     

               if (rect.width <= 0 || rect.height <= 0) {  // 잘못 두번쨰 좌표 선택한 경우 취소

                   step = -1;

                   rect.x = rect.y = rect.width = rect.height = 0;

                   matMask = null;

                   return false;

               }

     

               step = 2;

               matMask = null;

               matMask = Mat.zeros(matInput.size(), matInput.type());

               matMask.submat(rect).setTo(Scalar.all(255));

           }

     

           return false;

       }

     

    @Override
    
       public boolean onTouch (View v, MotionEvent event){
    
           int width = matInput.cols();
           int height = matInput.rows();
    
           // 카메라 뷰와 이미지 좌표 맞추기
           int xOffset = (mOpenCvCameraView.getWidth() - width) / 2;
           int yOffset = (mOpenCvCameraView.getHeight() - height) / 2;
    
           int x = (int) event.getX() - xOffset;
           int y = (int) event.getY() - yOffset;
    
           if (x < 0 || y < 0 || x >= width || y >= height) {// 터치가 범위 벗어난 경우
    
               step = -1;
               rect.x = rect.y = rect.width = rect.height = 0;
               matMask = null;
               return false;
           }
    
    
           if ( step == 2) { // ROI 설정된 상태에서 또 터치하면 설정 취소
               step = -1;
               rect.x = rect.y = rect.width = rect.height = 0;
               matMask = null;
               return false;
           }
    
    
           if ((step == -1)&&((rect.x == 0 && rect.y == 0) || (rect.width != 0 && rect.height != 0))) { //첫번째 클릭
    
               step = 1;
               matMask = null;
               rect.x = x;
               rect.y = y;
               rect.width = rect.height = 0;
    
           } else if ( step == 1){  //두번째 클릭
    
               rect.width = x - rect.x;
               rect.height = y - rect.y;
    
               if (rect.width <= 0 || rect.height <= 0) {  // 잘못 두번쨰 좌표 선택한 경우 취소
                   step = -1;
                   rect.x = rect.y = rect.width = rect.height = 0;
                   matMask = null;
                   return false;
               }
    
               step = 2;
               matMask = null;
               matMask = Mat.zeros(matInput.size(), matInput.type());
               matMask.submat(rect).setTo(Scalar.all(255));
           }
    
           return false;
       }




    추가 6 : native-lib.cpp

     

       Mat &matInput = *(Mat *)matAddrInput;

       Mat &matResult = *(Mat *)matAddrResult;

     

       cvtColor(matInput, matResult, COLOR_RGBA2GRAY);

       blur( matResult, matResult, Size(5,5));

       Canny( matResult, matResult, 50, 150);

     

       blur( matResult, matResult, Size(5,5));
       Canny( matResult, matResult, 50, 150);




    참고  

     

    https://answers.opencv.org/question/55734/opencv-area-selection-on-live-camera-feed/




    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    댓글 28

    • david 2019.05.15 11:33


      안녕하세요, 너무나도 유익한 강의 잘봤습니다. 한번에 이해할 수 있었습니다 ^^
      궁금한 점이 있는데요, opencv 에 있는 tracker (KCF, MOSSE,CSRT )등을 사용할 수 있을까요?
      contrib 안에 있는것으로 알고 있는데 따로 설치 하는 방법이 있는지 궁금합니다.
      감사합니다.

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.15 13:59 신고


        정확한건 테스트해봐야 겠지만..

        윈도우용으로 빌드되서 나오는 패키지에서 그런 것처럼

        사용하려면 contrib를 포함시켜 빌드해야 하지 않을까 싶습니다.

    • 학생 2019.06.26 09:30


      혹시 터치하지 않고 영상의 가운데 부분만 처리하려고 하면 어떻게하나요???

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.26 11:27 신고


        다음 부분만 사용하면 될듯합니다.
        마스크 이미지를 생성하는 코드입니다.

        Rect 타입의 rect에 원하는 좌표를 입력하면 됩니다.
        matMask.submat(rect).setTo(Scalar.all(255));

      • 학생 2019.06.26 18:03


        저 코드만 입력하면 되는건가요??
        함수를 만들어서 해야될거 같은데 어떻게 해야할지 모르겠네요.....

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.26 18:06 신고


        추가 4의 다음 코드 앞에 오면 됩니다.

        Core.bitwise_and(matInput, matMask, matInput1);

      • 학생 2019.06.26 18:33


        public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {

        matInput = inputFrame.rgba();

        //if ( matResult != null ) matResult.release(); fix 2018. 8. 18

        if ( matResult == null )

        matResult = new Mat(matInput.rows(), matInput.cols(), matInput.type());

        Core.bitwise_and(matInput, matMask, matInput1);
        ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());

        return matResult;
        }
        이렇게 추가시키면 되나요?
        Core에 빨간거 뜨는데..
        그리고 이것만하면 되나요? 다른 코드는 추가 안해도되나요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.26 18:45 신고


        다음처럼하고 Rect 타입의 rect 변수에 좌표만 넣어주면 될듯합니다.

        matMask = Mat.zeros(matInput.size(), matInput.type());
        matMask.submat(rect).setTo(Scalar.all(255));
        Core.bitwise_and(matInput, matMask, matInput);
        ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());

      • 학생 2019.06.26 18:57


        감사합니다!. 일단 성공했습니다. 근데 관심영역 외에는 카메라가 안잡히고 검은색으로 뜨는데
        카메라로 다 촬영후 관심영역만 뜨게 할려면 어떻게 해야하나요...

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.27 10:33 신고


        포스팅 코드를 참고하세요.

        지금 적용한 코드가 있는 부분 아래쪽에 있습니다.

      • 학생 2019.06.28 13:03


        matMask = Mat.zeros(matInput.size(), matInput.type());
        matMask.submat(rect).setTo(Scalar.all(255));
        Core.bitwise_and(matInput, matMask, matInput);
        ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());
        Core.bitwise_not(matMask, matNotMask);
        Core.bitwise_and(matInput, matNotMask, matInput2);
        Imgproc.cvtColor(matInput1, matInput1, Imgproc.COLOR_GRAY2RGBA);
        Core.bitwise_or(matInput1, matInput2, matResult);
        이렇게 했는데 안되네요...

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.28 13:08 신고


        PC에서 imshow함수로 동작 여부를 확인하고 안드로이드로 옮겨서 진행해보세요.

    • Denis KIM 2019.07.03 09:33


      안녕하세요? 고수님!
      하다보니 여기까지 오게 되었습니다.
      아마도 수 많은 사람들이 고수님의 강의를 보고 듣고 할 것입니다.
      오늘은
      혹 관심영역을 좌표로 지정하는 강의는 없으신지요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.03 09:50 신고


        변수 rect에 지정한 좌표가 저장됩니다.
        이 변수에 원하는 좌표를 지정하면 됩니다.

        아직 고정된 영역으로 동작하도록 작성한 포스트 또는 유튜브 영상은 없습니다

      • Denis KIM 2019.07.04 16:53


        고수님! 감사 합니다.

      • Denis KIM 2019.07.04 18:05


        public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {

        matInput = inputFrame.rgba();

        //if ( matResult != null ) matResult.release(); fix 2018. 8. 18

        if ( matResult == null )

        matResult = new Mat(matInput.rows(), matInput.cols(), matInput.type());

        matMask = Mat.zeros(matInput.size(), matInput.type());
        //matMask.submat(rect).setTo(Scalar.all(255));
        matMask.submat(10,10,30,50).setTo(Scalar.all(255));
        Core.bitwise_and(matInput, matMask, matInput);
        ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());

        return matResult;
        }

        이렇게 했는데 자동 종료 되는데 맞는지요? 한수 부탁드립니다.

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.04 18:12 신고


        matResult 영상은 원본 영상과 크기가 같아야 합니다.

    • Pase 2019.07.11 16:28


      안녕하세요 ROI영역 설정하는거 까지 했는데 어플실행후 몇초 있다가 강제 종료가 됩니다.
      다른 사람 말로는 영상 버퍼를 계속 만들다가 한계가 되서 꺼지는거같은데 혹시 안꺼지게 할 수 있는 방법이있나요??

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.11 19:31 신고


        로그캣에서 에러 메시지를 찾아야 원인을 알 수 있습니다.

      • Pase 2019.07.17 15:41


        로그캣을 보고있는데 로그가 끊임없이 올라가서 볼수가없는데 로그캣 보는 방법 알려주실수있나요??

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.17 17:42 신고


        이벤트 발생시 발생하는 에러는

        마우스 우클릭하여 메뉴에서 clear logcat을 선택하여 다지우고...

        앱에서 동작시 발생하는 에러를 확인하면 됩니다.


        알수 없는 이유로 발생하는 오류는
        로그캣 창 상단에 보이는 콤보박스를
        Verbose에서 Error로 바꾸고 찾으면 됩니다.


        오른쪽 끝에 있는 콤보 박스는
        show only selected application을 선택해야합니다.



    • mj 2019.07.23 21:40


      안녕하세요 게시글 잘 봤습니다 ^^. 질문사항 있어 댓글 남깁니다.
      저는 ROI 좌표를 가지고 있는 상태일 때, ROI영역만 분리해서 처리하고 싶습니다.

      게시글에서는 마우스 클릭으로 ROI를 지정해주신 다음 그 부분만 처리를 하셨는데
      ROI를 마우스 클릭으로 지정이 아닌 좌표값을 통해 지정을 할 수 있을까요?
      좌표값을 통해 넘어오는 ROI 영역만을 따로 처리해주고 싶습니다.

      아래의 댓글들을 보고 참고하였는데도 해결되지 않아 글 남깁니다 ㅠㅠ

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.23 22:20 신고


        아래 코드가 지정한 영역(rect)를 가지고 마스크 이미지를 만드는 부분입니다.

        matMask = Mat.zeros(matInput.size(), matInput.type());

        matMask.submat(rect).setTo(Scalar.all(255));

        위 코드를 사용하는 부분이 다음 부분입니다.
        if (matMask != null ) {

        Core.bitwise_and(matInput, matMask, matInput1);

        ConvertRGBtoGray(matInput1.getNativeObjAddr(), matInput1.getNativeObjAddr());



        if ( matMask == null || matNotMask == null ){

        return matInput;

        }



        Core.bitwise_not(matMask, matNotMask);

        Core.bitwise_and(matInput, matNotMask, matInput2);



        Imgproc.cvtColor(matInput1, matInput1, Imgproc.COLOR_GRAY2RGBA);



        Core.bitwise_or(matInput1, matInput2, matResult);

    • Pase 2019.07.31 19:42


      혹시 roi 설정하는걸 c++에서 할 수 있나요???

    • jj 2019.11.20 20:38


      포스티 잘보았습니다 항상 감사드려요!
      근데, 터치를 하지 않고 항상 관심영역에서 특정 rgb를 탐지할 경우를 다루고 싶어서
      위에 댓글 참고하여 다음과 같이 코드를 적용했습니다

      if ( matResult == null )

      matResult = new Mat(matInput.rows(), matInput.cols(), matInput.type());

      matMask = Mat.zeros(matInput.size(), matInput.type());
      matMask.submat(rect).setTo(Scalar.all(255));
      Core.bitwise_and(matInput, matMask, matInput1);

      ConvertRGBtoGray(matInput.getNativeObjAddr(), matResult.getNativeObjAddr());

      Core.bitwise_not(matMask, matNotMask);
      Core.bitwise_and(matInput, matNotMask, matInput2);
      Imgproc.cvtColor(matInput1, matInput1, Imgproc.COLOR_GRAY2RGBA);
      Core.bitwise_or(matInput1, matInput2, matResult);


      return matResult;

      이 때, 빌드 오류는 없는데, 어플리케이션이 강제종료 됩니다. 어떤게 문제인지 자세히 알려주실 수 있을까요?

    • 2020.01.24 01:20


      비밀댓글입니다

Designed by Tistory.