OpenCV 어플리케이션 만들기

2019. 8. 29. 16:08App 개발/OpenCV for Android

준비

운영체제 : Windows 10

개발환경 : Andriod Studio

 

OpenCV를 사용해 간단한 어플리케이션을 제작합니다. (그레이스케일, HSV, Smoothing 필터 구현)

구현할 영상처리 흐름도

안드로이드에서 OpenCV를 사용하기 위해서는 준비과정이 필요합니다.

크게 NDK방법과 Java api 방법으로 나뉩니다.

NDK방법은 C++를 사용하고 준비과정이 조금 더 복잡합니다.

 

Java api를 사용했습니다.

 

OpenCV Java api 세팅방법

 

Android Studio 2.2에서 OpenCV 3.1 세팅하기

OpenCV와 Android Studio 버전에 따라 다른 내용이 생길 수 있으니 주의 바랍니다. 1. 안드로이드 SDK 다운로드 OpenCV 다운로드 페이지에서 OpenCV for Android를 다운 받습니다. 다운 받은 파일을 원하는 위치에 압축 해제합니다. 저는 편리한 접근을 위해 C 드라이브에 바로 압축해제 했습니다. 2. 프로젝트 생성 새롭…

blog.qwaz.io


UI 디자인

화면과 필터를 바꿔줄 버튼만 있으면 되기 때문에 아래와 같이 간단하게 UI를 만들었습니다.

위의 준비과정을 통해 제대로 세팅 했다면 JavaCameraView를 사용 할 수 있습니다.

카메라를 사용해야 하기 때문에 카메라 퍼미션을 줘야합니다.

<uses-permission android:name="android.permission.CAMERA"></uses-permission>

UI 디자인

 

레이아웃 소스코드

 

더보기
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <org.opencv.android.JavaCameraView
        android:id="@+id/camera_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

    <Button
        android:id="@+id/Button0"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_below="@+id/Button3"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginTop="9dp"
        android:layout_marginEnd="3dp"
        android:layout_marginRight="3dp"
        android:background="#7d010000"
        android:text="No"
        android:textColor="#ffffff"
        android:textSize="13dp" />

    <Button
        android:id="@+id/Button1"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginEnd="3dp"
        android:layout_marginRight="3dp"

        android:background="#7d010000"
        android:text="HSV"
        android:textColor="#ffffff"
        android:textSize="13dp" />

    <Button
        android:id="@+id/Button2"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_above="@+id/Button1"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginEnd="3dp"
        android:layout_marginRight="3dp"
        android:layout_marginBottom="9dp"
        android:background="#7d010000"
        android:text="Smooth"
        android:textColor="#ffffff"
        android:textSize="13dp" />

    <Button
        android:id="@+id/Button3"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_below="@+id/Button1"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginTop="9dp"
        android:layout_marginEnd="3dp"
        android:layout_marginRight="3dp"
        android:background="#7d010000"
        android:text="Gray"
        android:textColor="#ffffff"
        android:textSize="13dp" />

    <Button
        android:id="@+id/Button4"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_above="@+id/Button2"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginEnd="3dp"
        android:layout_marginRight="3dp"
        android:layout_marginBottom="9dp"
        android:background="#7d010000"
        android:text="ROI"
        android:textColor="#ffffff"
        android:textSize="13dp" />

</RelativeLayout>

버튼 이벤트

버튼 이벤트로 한 버튼을 클릭하면 클릭된 버튼만 1값을 가지고 나머지는 0값을 가지도록 했습니다.

우선 버튼의 변수를 선언하고 레이아웃에 있는 버튼과 연결시켜줍니다.

//변수선언    
    Button Button0;
    Button Button1;
    Button Button2;
    Button Button3;
    Button Button4;
    Mat image, mat1,mat2, gray;
    Scalar scalarLow,scalarHigh;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//변수에 버튼 등록
        Button0 = (Button) findViewById(R.id.Button0);
        Button1 = (Button) findViewById(R.id.Button1);
        Button2 = (Button) findViewById(R.id.Button2);
        Button3 = (Button) findViewById(R.id.Button3);
        Button4 = (Button) findViewById(R.id.Button4);
}

 

버튼의 setOnClickListener

더보기
        Button0.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v){
                Toast.makeText(MainActivity.this, "No", Toast.LENGTH_SHORT).show();
                RGBA=1;
                GrayScale=0;
                HSV=0;
                Smoothing=0;
                ROI=0;
            }

        });
        Button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v){
                Toast.makeText(MainActivity.this, "HSV", Toast.LENGTH_SHORT).show();
                RGBA=0;
                GrayScale=0;
                HSV=1;
                Smoothing=0;
                ROI=0;
            }
        });
        Button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v){
                Toast.makeText(MainActivity.this, "Smooth", Toast.LENGTH_SHORT).show();
                RGBA=0;
                GrayScale=0;
                HSV=0;
                Smoothing=1;
                ROI=0;

            }
        });
        Button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v){
                Toast.makeText(MainActivity.this, "Gray", Toast.LENGTH_SHORT).show();
                GrayScale=1;
                RGBA=0;
                HSV=0;
                Smoothing=0;
                ROI=0;
            }

        });
        Button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v){
                Toast.makeText(MainActivity.this, "ROI", Toast.LENGTH_SHORT).show();
                GrayScale=0;
                RGBA=0;
                HSV=0;
                Smoothing=0;
                ROI=1;
            }
        });

    }

필터적용

필터는 이미 라이브러리에 있기 때문에 불러서 쓰기만 하면 됩니다.

GrayScale, HSV, Smoothing 필터를 사용했고 

원래의 화면을 보여주기 위해 rgba

원하는 부분의 색상만 보여주기위해 HSV + inRange를 사용했습니다.

public Mat onCameraFrame(final CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        image = inputFrame.rgba();

        if (GrayScale==1)
        {
            image = inputFrame.gray();
        }
        if (RGBA==1)
        {
            image = inputFrame.rgba();
        }
        if (HSV==1)
        {

            Imgproc.cvtColor(inputFrame.rgba(), mat1, Imgproc.COLOR_RGB2HSV);
            Core.inRange(mat1,scalarLow,scalarHigh,mat2);
            //inRange 함수는 그 범위안에 들어가게되면 0으로 만들어주고 나머지는 1로 만들어 흑백사진을 만든다.
            image = mat2;
        }
        if (Smoothing==1)
        {
            org.opencv.core.Size s = new Size(21,21);
            //Imgproc.GaussianBlur(image, image, s, 0,0);
            Imgproc.boxFilter(image,image,-1,s);
        }
        if (ROI==1) {
            Imgproc.cvtColor(inputFrame.rgba(), mat1, Imgproc.COLOR_RGB2HSV);
            Core.inRange(mat1,scalarLow,scalarHigh,mat2);
            //inRange 함수는 그 범위안에 들어가게되면 0으로 만들어주고 나머지는 1로 만들어 흑백사진을 만든다.
            Core.bitwise_and(image, image, mat1, mat2);
            image = mat1;
        }
        return image;
    }

HSV는 변수로 Low값과 High값을 미리 아래와 같이 설정했습니다.

scalarLow = new Scalar(120-10,30,30);
scalarHigh = new Scalar(120+10,255,255);
//red scalarLow = new Scalar(0, 30, 30), scalarHigh = new Scalar(10, 255, 255)
//green scalarLow = new Scalar(45, 30, 30), scalarHigh = new Scalar(75, 255, 255)
//blue scalarLow = new Scalar(110, 30, 30), scalarHigh = new Scalar(130, 255, 255)

HSV 영상처리

사람의 피부색이나 도로의 차선을 인식하는데 사용되는HSV영상처리는 원하는 색을 검출하는데 사용합니다.

RGB 달리 색도(Hue), 채도(Saturation), 명도(Value) 3요소로 구성되므로 보다 정교하게 원하는 색을 검출합니다.

HSV색공간의 색도(Hue)성분은 특정색의 컬러가 일정한 범위를 가지기 때문에 원하는 색의 범위를 정하기 쉽습니다.

색도는 0도부터 359도까지 범위에 색을 일정한 간격으로 배치한 것입니다.

하지만 OpenCV에서는 0도에서 179도까지 범위를 가지기 때문에 0.5를 곱해서 사용했습니다.

채도(Saturation) 색상의 진함을 의미합니다. 값이 클수록 진한 색을 의미하고 값이 작아질수록 흰색에 가까워집니다.

OpenCV에서는 0에서 255까지의 범위를 가집니다.

명도(Value) 색상의 밝은 정도를 의미합니다. 검은색을 0으로했을 값이 커질수록 밝아지고 0 가까울 수록 검은색과 가까워집니다. OpenCV에서는 0에서 255까지의 범위를 가집니다.

 

너무 어둡거나 밝은 색을 제외하기 위해서 명도와 채도 값은 30~255범위로했고

색검출을 위해 110~130의 범위를 줬습니다.


결과 화면

No 버튼
Gray 버튼
Smooth 버튼
HSV 버튼
ROI 버튼


 

Yeowoolee/Android-OpenCV-app

Contribute to Yeowoolee/Android-OpenCV-app development by creating an account on GitHub.

github.com