안드로이드 OpenCV의 findContours 사용중 다음과 같은 오류가 발생했을 때 해결하는 방법입니다.

오류 :

Caused by: CvException [org.opencv.core.CvException: cv::Exception: OpenCV(3.4.3) /build/3_4_pack-android/opencv/modules/imgproc/src/contours.cpp:199: error: (-210:Unsupported format or combination of formats) [Start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL otherwise supports CV_32SC1 images only in function '_CvContourScanner* cvStartFindContours_Impl(void*, CvMemStorage*, int, int, int, CvPoint, int)'


오류가 나는 이유는 findContours의 입력 값과 출력 값의 데이터 옵션 값이 다르기 때문입니다.


해결 방법 : 

Imgproc.findContours를 호출하기 전

기존의 Mat result 객체를 다음과 같이 처리해줍니다.

Imgproc.cvtColor(src, result, Imgproc.COLOR_BGR2GRAY, 1);


오류해결!


* 참고

문자인식 중 findContours가 이진화(threshold) 된 이미지의 개체들의 blob을 찾지 못할 때 : 이진화 된 이미지의 픽셀 값(색상)을 모두 반전 시켜줍니다.

코드 : 

for(int x=0; x<roi.getWidth(); x++) {

    for(int y=0; y<roi.getHeight(); y++) {

        if(roi.getPixel(x, y) == -1) {

            roi.setPixel(x, y, 0);

        }

        else {

            roi.setPixel(x, y, -1);

        }

    }

}





정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.




이번 시간에는 OpenCV를 이용하여 관심역영(ROI)을 추출하겠습니다.

관심영역 추출은 문자인식 전처리 기법 중 하나로

인식 할 범위를 대폭 축소시켜 프로그램 전반적인 실행속도를 향상 시키고

인식률 또한 높일 수 있는 전처리 기법입니다.


※ 안드로이드와 OpenCV 연동은 이곳을 참고하세요.

※ SurfaceView와 카메라 연동은 이곳을 참고하세요.


동작 과정

1. SurfaceView에 카메라 화면 출력

2. 캡쳐를 누르면 안드로이드 내부 저장소에 사진이 저장

3. 새로운 액티비티가 실행되며 저장소에 저장된 사진을 불러옴

4. 불러온 사진을 흑백처리, 이진화처리 수행

5. 처리된 사진을 OpenCV 라이브러리 함수로 영역들을 추출해냄

6. 추출된 영역 중 자동차 번호판에 해당하는 영역만 표시

7. 표시된 영역을 따로 결과화면에 출력

8. 출력된 관심영역을 가지고 문자인식 수행



소스코드 입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
List<MatOfPoint> contours = new ArrayList<>(); 
 
Mat hierarchy = new Mat();
 
Imgproc.findContours(imageCny1, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
 
 
for(int idx = 0; idx >= 0; idx = (int) hierarchy.get(0, idx)[0]) {
 
MatOfPoint matOfPoint = contours.get(idx);
 
Rect rect = Imgproc.boundingRect(matOfPoint);
 
if(rect.width < 30 || rect.height < 30 || rect.width <= rect.height || rect.x < 20 || rect.y < 20
 
|| rect.width <= rect.height * 3 || rect.width >= rect.height * 6continue// 사각형 크기에 따라 출력 여부 결정
 
 
 
// ROI 출력
 
Bitmap roi = Bitmap.createBitmap(myBitmap, (int)rect.tl().x, (int)rect.tl().y, rect.width, rect.height);
 
ImageView imageView1 = (ImageView)findViewById(R.id.image_result_ROI);
 
imageView1.setImageBitmap(roi);
 
}
 
 
image1= Bitmap.createBitmap(img1.cols(), img1.rows(), Bitmap.Config.ARGB_8888);
 
Utils.matToBitmap(img1, image1); // Mat to Bitmap
 
 
imageView = (ImageView)findViewById(R.id.image_result);
 
imageView.setImageBitmap(image1);
 
}
cs



실행화면 입니다.


  





정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.







안드로이드에 OpenCV를 설치하는 방법에 대해 알아봅시다.

아래에 링크에 들어가서 OpenCV 3.4.3 android sdk를 다운받고 적절한 폴더에 설치합니다.

https://github.com/opencv/opencv/releases

OpenCV를 설치할 프로젝트를 열고

File-New-Import Module을 선택합니다.



방금 설치한 OpenCV 폴더로 이동해서

sdk/java를 선택하고 OK를 눌러줍니다.

제대로 된 폴더를 선택했다면 OpenCV라는 모듈명이 나옵니다.



모듈을 추가하고 app/build.gradle에 들어가서 맨 아래 프로젝트 추가 코드를 입력합니다.


아래는 이미지파일을 비트맵으로 받아와 흑백 및 에지검출 소스코드입니다.


File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "이미지 파일 경로.png"); // 파일 불러오기

if(file.exists()){ // 파일이 존재한다면

    Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); // 비트맵 생성


    Bitmap image1;

    OpenCVLoader.initDebug(); // 이 코드를 선언해주지않으면 컴파일 에러 발생


    Mat img1=new Mat();

    Utils.bitmapToMat(myBitmap ,img1);

    Mat imageGray1 = new Mat();

    Mat imageCny1 = new Mat();


    //Imgproc.cvtColor(img1, imageGray1, Imgproc.COLOR_BGR2GRAY); // GrayScale


    //Imgproc.Canny(imageGray1, imageCny1, 10, 100, 3, true); // Canny Edge 검출


    //Imgproc.threshold(imageGray1, imageCny1, 150, 255, Imgproc.THRESH_BINARY); //Binary


    image1= Bitmap.createBitmap(imageCny1.cols(), imageCny1.rows(), Bitmap.Config.ARGB_8888); // 비트맵 생성

    Utils.matToBitmap(imageCny1, image1); // Mat을 비트맵으로 변환


    imageView = (ImageView)findViewById(R.id.image_result);

    imageView.setImageBitmap(image1); // 이미지 뷰에 비트맵 출력

}





정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.




안드로이드로 핀치 줌 (두 손가락으로 벌리고 좁힘으로써 화면을 확대, 축소 하는 기능)을 멀티 터치 이벤트로 구현하는 방법입니다.

먼저 변수 선언입니다. (클래스 내부입니다.)


private double touch_interval_X = 0; // X 터치 간격

private double touch_interval_Y = 0; // Y 터치 간격

private int zoom_in_count = 0; // 줌 인 카운트

private int zoom_out_count = 0; // 줌 아웃 카운트

private int touch_zoom = 0; // 줌 크기


X, Y 터치 간격은 멀티 터치 이벤트에서 구현 한 두 손가락 사이의 X, Y값을 받아올 변수입니다.

이는 이전 값 저장 후 현재 값과 비교용으로 선언했습니다.

줌 인, 아웃 카운트는 터치이벤트가 너무 자주 발생하기 때문에 카운트를 세어서 확대 및 축소를 하기위한 변수입니다.

줌 크기는 실제로 확대 및 축소 된 값을 카메라에게 전달하기 위한 변수입니다.


Generate를 통해 Override Methods - onTouchEvent를 생성합니다.


* 만약 Layout에 클릭 리스너 이벤트가 존재한다면 제대로 실행 되지 않을 수 있습니다!


onTouchEvent 함수를 만들고 다음과 같이 작성합니다.


@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()  & MotionEvent.ACTION_MASK) {

case MotionEvent.ACTION_DOWN: // 싱글 터치

CameraPreview.mCamera.autoFocus(new Camera.AutoFocusCallback(){ // 오토 포커스 설정

@Override

public void onAutoFocus(boolean success, Camera camera) {

return;

}

});

break;


case MotionEvent.ACTION_MOVE: // 터치 후 이동 시

if(event.getPointerCount() == 2) { // 터치 손가락 2개일 때

double now_interval_X = (double) abs(event.getX(0) - event.getX(1)); // 두 손가락 X좌표 차이 절대값

double now_interval_Y = (double) abs(event.getY(0) - event.getY(1)); // 두 손가락 Y좌표 차이 절대값

if(touch_interval_X < now_interval_X && touch_interval_Y < now_interval_Y) { // 이전 값과 비교

// 여기에 확대기능에 대한 코드를 정의 하면됩니다. (두 손가락을 벌렸을 때 분기점입니다.)

zoom_in_count++;

if(zoom_in_count > 5) { // 카운트를 세는 이유 : 너무 많은 호출을 줄이기 위해

zoom_in_count = 0;


touch_zoom += 5;

ZoomseekBar.setProgress(touch_zoom/6);


if(CameraPreview.params.getMaxZoom() < touch_zoom)

touch_zoom = CameraPreview.params.getMaxZoom();


CameraPreview.params.setZoom(touch_zoom);

CameraPreview.mCamera.setParameters(CameraPreview.params);

}

}

if(touch_interval_X > now_interval_X && touch_interval_Y > now_interval_Y) {

// 여기에 축소기능에 대한 코드를 정의 하면됩니다. (두 손가락 사이를 좁혔을 때 분기점입니다.)

zoom_out_count++;

if(zoom_out_count > 5) {

zoom_out_count = 0;


touch_zoom -= 10;

ZoomseekBar.setProgress(touch_zoom/6);


if(0 > touch_zoom)

touch_zoom = 0;


CameraPreview.params.setZoom(touch_zoom);

CameraPreview.mCamera.setParameters(CameraPreview.params);

}

}

touch_interval_X = (double) abs(event.getX(0) - event.getX(1));

touch_interval_Y = (double)  abs(event.getY(0) - event.getY(1));

}

break;

//            case MotionEvent.ACTION_POINTER_DOWN : // 여러개 터치했을 때

//                Log.d(TAG, "멀티터치 : " + event.getX() + " , " + event.getY());

//                break;

//            case MotionEvent.ACTION_UP: // 터치 뗐을 때

//                break;

}

return super.onTouchEvent(event);

}


여기까지 안드로이드 핀치 줌을 멀티 터치 이벤트로 구현하는 방법이었습니다.






정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.



ScorllView에 ImageView를 넣었을 때 위 아래로 여백이 생기는 것을 확인했습니다.

이를 제거하기 위해 ScaleType을 모두 적용했지만 원하는데로 적용되지 않았습니다.


해결 방법은 간단합니다.

스크롤 뷰, 이미지 뷰 레이아웃 너비, 높이를 모두 match_parent로 설정하고

이미지 뷰에 다음과 같이 추가합니다.


android:adjustViewBounds="true"


이미지 보다 레이아웃이 더 클 때 비율을 유지하는 여부를 묻는 속성입니다.

true로 설정하면 이미지가 레이아웃에 딱 맞게 설정되어 표시됩니다.


아래 전체 xml 코드입니다.


<ScrollView

android:layout_width="match_parent"

android:layout_height="match_parent">

<ImageView

android:src="@drawable/..."

android:layout_width="match_parent"

android:layout_height="match_parent"

android:adjustViewBounds="true"/>

</ScrollView>


이상 이미지 뷰 여백 없애기였습니다.




정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.



to Top