티스토리 뷰

반응형

개발도 사회 분위기를 따라가며 업무가 주어지게 됩니다.

요즘 시대는 영상과 미디어에 민감하게 반응 하고 있습니다. 그렇다보니 App 또한 영상을 활용한 컨텐츠가 많이 나오고 있습니다.

저도 최근에 영상을 관련한 업무를 맡게 되면서 어떻게 처리해야할지 고민을 했었습니다.

NDK로 활용한 프로젝트는 선택이었었는데 이제는 필수가 되어 제가 공부한 것을 조금씩 나누려고 합니다.

잘못된 정보와 질문 환영 합니다.

 

 

OpenCV 그게 뭔데?

 https://opencv.org/

OpenCV 을 활용하기 전에 OpenCV가 무엇인지 논하고 가겠습니다.

OpenCV에 대한 정의를 위키백과에서는 다음과 같이 설명하고 있습니다.

 

OpenCV OpenCV(Open Source Computer Vision)은 주로 실시간 컴퓨터 비전을 목적으로 한 프로그래밍 라이브러리이다. 원래는 인텔이 개발하였다. 실시간 이미지 프로세싱에 중점을 둔 라이브러리이다. 인텔 CPU에서 사용되는 경우 속도의 향상을 볼 수 있는 IPP(Intel Performance Primitives)를 지원한다. 이 라이브러리는 윈도, 리눅스 등에서 사용 가능한 크로스 플랫폼이며 오픈소스 BSD 허가서 하에서 무료로 사용할 수 있다. OpenCV는 TensorFlow , Torch / PyTorchCaffe딥러닝 프레임워크를 지원한다.

 

한줄로 요약하면 실시간 이미지 프로세싱에 중점을 둔 라이버리로 정리할 수 있습니다.

이미지 프로세싱이 필요한 경우 OpenCV로 하면 효율적으로 활용 할 수 있다는 점만 기억하고 간단한 Demo를 만들어 보겠습니다.

 

 

이미지 Canny 효과 주기 - 소스 코드

 

이미지에 Canny 효과 준 전체 소스는 다음과 같습니다.

 

MainActivity.java

package com.photo.opencv_001;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getName();
    private final int REQ_CODE_SELECT_IMAGE = 100;

    private ImageView imageView;
    private Bitmap bitmap;

    static {
        if(!OpenCVLoader.initDebug()){
            Log.d(TAG, "OpenCV is not loaded!");
        }else {
            Log.d(TAG, "OpenCV is loaded successfully!");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            if(!hasPermissions(PERMISSIONS)){
                requestPermissions(PERMISSIONS, PERMISSIONS_REQUEST_CODE);
            }
        }

        imageView = findViewById(R.id.image_view_01);
    }

    @Override
    protected void onDestroy() {
        bitmap.recycle();
        bitmap = null;
        super.onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if(requestCode == REQ_CODE_SELECT_IMAGE){
            if(resultCode == RESULT_OK){
                try{
                    String path = getImagePathFromURI(data.getData());

                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inSampleSize = 4;
                    bitmap = BitmapFactory.decodeFile(path, options);

                    if(bitmap != null){
                        detectEdge();
                        imageView.setImageBitmap(bitmap);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    static final int PERMISSIONS_REQUEST_CODE = 1000;
    String[] PERMISSIONS = {"android.permission.READ_EXTERNAL_STORAGE"};
    private boolean hasPermissions(String[] permissions) {
        int result;
        for (String perms : permissions){
            result = ContextCompat.checkSelfPermission(this, perms);
            if (result == PackageManager.PERMISSION_DENIED){ return false;
            } }
        return true;
    }

    public String getImagePathFromURI(Uri contentUri){
        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
        if (cursor == null){
            return null;
        }else {
            int idx = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            String imgPath = cursor.getString(idx);
            cursor.close();
            return imgPath;
        }
    }

    /***
     * 디바이스에 있는 이미지 가져오기
     */
    public void onButtonClicked(View view){
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
        intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, REQ_CODE_SELECT_IMAGE);
    }

    /***
     * OpenCV Source
     */
    public void detectEdge(){
        Mat src = new Mat();
        Utils.bitmapToMat(bitmap, src);

        Mat edge = new Mat();
        Imgproc.Canny(src, edge, 50, 150);

        Utils.matToBitmap(edge, bitmap);

        src.release();
        edge.release();
    }
}

 

 

간단한 소스 맛보기

 

OpenCV 사이트 에서는 Window, iOS, Android 에서 활용할 수 있도록 Open Source를 제공하고 있습니다.

이번 소개한 소스에는 Android Open Source를 활용하여 사진을 가져와 Canny 효과를 주었습니다.

Canny 효과를 주기 위해서는 OpenCV 환경 구성 → 디바이스에서 이미지 가져오기 → Canny 효과 주기 → 이미지 표시하기 순으로 진행됩니다.

 

OpenCV 환경 구성하기

Android Project에 OpenCV 환경 구성이 되었는지 확인할 때는 OpenCVLoader.initDebug() 메소를 호출하여 확인할 수 있습니다.

static {
    if(!OpenCVLoader.initDebug()){
        Log.d(TAG, "OpenCV is not loaded!");
    }else {
        Log.d(TAG, "OpenCV is loaded successfully!");
    }
}

 

이미지 가져오기

Canny 효과를 줄 이미지를 가져오는 로직은 Intent.ACTION_PICK을 활용하여 사진을 가져오도록 구성하였습니다.

 

Canny 효과 주기

Canny 효과를 주는 방법은 이미지 가져온 이미지를 Bitmap 으로 변환 시킵니다.

변환 시킨 Bitmap을 OpenCV의 Utilsclass bitmapToMat 메소드를 활용하여 Mat 형태로 변환 시킵니다.

변환 된 Mat 데이터를 Imgproc.Canny 메소드를 활용하여 Canny 효과를 적용 시킨 후 matToBitmap 메소드로 다시 Bitmap으로 변환 시킵니다.

변환 하면서 생성한 Mat class 들은 release 하여 메모리 관리를 해줍니다.

 public void detectEdge(){
        Mat src = new Mat();
        Utils.bitmapToMat(bitmap, src);

        Mat edge = new Mat();
        Imgproc.Canny(src, edge, 50, 150);

        Utils.matToBitmap(edge, bitmap);

        src.release();
        edge.release();
    }

 

 

마무리

 

요즘 다양한 사진 앱들은 이미지 프로세싱 기술을 활용하여 다양한 효과와 재미를 주고 있습니다.

다양한 이미지 프로세싱 기술이 존재하는데 가장 간단하면서 빠르게 접근할 수 있는 방법은 OpenCV을 활용하는 것입니다.

물론 OpenCV에서 제공하는 기술만을 갖고는 소비자들에게 만족스로운 제품을 제공 못할 수도 있습니다.

소비자에게 만족스러운 서비스를 제공하기 위해서 다양한 IT 업계에서는 연구와 노력을 하고 있습니다.

 

이번 포스트에서는 OpenCV의 대표 예제인 Canny 기능 소개하였습니다.

Canny 효과를 주는데 사용한 Improc 는 Canny 기능 외에도 다양한 기능을 제공하고 있습니다.

Improc 에서 제공하는 다양한 기능에 대한 설명은 Improc Documents 에서 확인할 수 있습니다.

 

어렵게만 느껴졌던 OpenCV에 한발 움직인 저와 이 글을 읽으신 모든 분들을 응원합니다.

 

반응형

'프로그래밍 > OpenCV' 카테고리의 다른 글

Android OpenCV NDK 설치 방법  (0) 2019.08.15
댓글