2019. 8. 29. 16:08ㆍApp 개발/OpenCV for Android
준비
운영체제 : Windows 10
개발환경 : Andriod Studio
OpenCV를 사용해 간단한 어플리케이션을 제작합니다. (그레이스케일, HSV, Smoothing 필터 구현)
안드로이드에서 OpenCV를 사용하기 위해서는 준비과정이 필요합니다.
크게 NDK방법과 Java api 방법으로 나뉩니다.
NDK방법은 C++를 사용하고 준비과정이 조금 더 복잡합니다.
Java api를 사용했습니다.
OpenCV Java api 세팅방법
UI 디자인
화면과 필터를 바꿔줄 버튼만 있으면 되기 때문에 아래와 같이 간단하게 UI를 만들었습니다.
위의 준비과정을 통해 제대로 세팅 했다면 JavaCameraView를 사용 할 수 있습니다.
카메라를 사용해야 하기 때문에 카메라 퍼미션을 줘야합니다.
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
레이아웃 소스코드
<?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의 범위를 줬습니다.
결과 화면