반응형



Android NDK와 OpenCV를 사용하여  갤러리에 있는 이미지를 불러와 영상처리하는 예제입니다. 

바를 이용하여 Canny의 스레숄드를 변경하면 바로 결과를 볼 수 있습니다. 



영상에는 코드 설명이 포함되어 있습니다.







외장 저장공간 접근 권한을 사용하기 때문에  처음 실행하면 권한 요청을 합니다. 





상단에 보이는 디폴트 이미지를 클릭하면 갤러리에서 이미지를 불러올 수 있습니다. 

 

 



이미지를 불러올 때 사용할 앱을 선택합니다.





갤러리에서 이미지를 선택합니다.





상단에 갤러리에서 선택한 이미지가 보입니다.





에지 검출하기를 선택하면 영상에서 에지를 검출하여 보여줍니다.





버튼 위에 보이는 두 개의 바를 사용하여 스레숄드 값을 변경할 수 있습니다.

바를 이동할때 마다 해당 스레숄드를 사용한 Canny의 에지 검출 결과를 보여줍니다.




 

2016.12.16  최초작성 

. . . .

2019. 2. 8.  갤러리에서 이미지 불러오도록 수정

2019. 5. 5.  스레숄드 조정할 수 있는 바를 추가  

2020. 9. 22 androidx 관련 수정




1.  다음 포스팅을 참고하여 안드로이드 프로젝트를 생성합니다.



Android NDK + CMake + OpenCV 카메라 예제 및 프로젝트 생성방법(Android Camera Example with NDK, OpenCV, CMake )

http://webnautes.tistory.com/1054  





2. AndroidManifest.xml 파일에서 필요없는 권한들을 제거하고  외부 저장소 접근 권한을 추가합니다. 

activity 태그에 있던 옵션을 제거합니다. 


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tistory.webnautes.useopencvwithcmake">
 

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


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>




3. build.gradle에서 최소 SDK을 26으로 수정한 후, Sync Now를 클릭하여 적용합니다. 

SeekBar에 최소값을 지정하려면 변경해야 합니다.



android {

    compileSdkVersion 30

    buildToolsVersion "30.0.2"


    defaultConfig {

        applicationId "com.tistory.webnautes.useopencvwithcmake"

        minSdkVersion 26

        targetSdkVersion 30

        versionCode 1

        versionName "1.0"


        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        externalNativeBuild {

            cmake {

                cppFlags ""

            }

        }

    }




4. activity_main.xml 파일을 다음 내용으로 대체합니다. 


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <ImageView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.4"
        android:id="@+id/imageViewInput"
        app:srcCompat="@drawable/ic_image"/>


    <ImageView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.4"
        android:id="@+id/imageViewOutput"/>

    <LinearLayout
        android:layout_height="0dp"
        android:layout_weight="0.06"
        android:layout_width="match_parent"
        android:orientation="horizontal">
        <SeekBar
            android:id="@+id/seekBar_threshold1"
            android:layout_width="0dp"
            android:layout_weight="0.9"
            android:layout_height="match_parent"/>
        <TextView
            android:id="@+id/textView_threshold1"
            android:layout_width="0dp"
            android:layout_weight="0.1"
            android:layout_height="match_parent" />

    </LinearLayout>

    <LinearLayout
        android:layout_height="0dp"
        android:layout_weight="0.06"
        android:layout_width="match_parent"
        android:orientation="horizontal">
        <SeekBar
            android:id="@+id/seekBar_threshold2"
            android:layout_width="0dp"
            android:layout_weight="0.9"
            android:layout_height="match_parent"/>
        <TextView
            android:id="@+id/textView_threshold2"
            android:layout_width="0dp"
            android:layout_weight="0.1"
            android:layout_height="match_parent" />

    </LinearLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.1"
        android:text="에지 검출하기" />


</LinearLayout>




5. MainActivity.java 파일을 다음 내용으로 변경합니다.

붉은색으로 표시되는 imageprocessing 함수에 마우스 커서를 가져가 빨간 전구가 보이면 Create JNI function을 클릭합니다. 


import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.w3c.dom.Text;

import java.io.IOException;


public class MainActivity extends AppCompatActivity {


    static {
        System.loadLibrary("opencv_java4");
        System.loadLibrary("native-lib");
    }


    ImageView imageVIewInput;
    ImageView imageVIewOuput;
    private Mat img_input;
    private Mat img_output;
    private int threshold1=50;
    private int threshold2=150;

    private static final String TAG = "opencv";
    private final int GET_GALLERY_IMAGE = 200;

    boolean isReady = false;


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

        imageVIewInput = (ImageView)findViewById(R.id.imageViewInput);
        imageVIewOuput = (ImageView)findViewById(R.id.imageViewOutput);

        Button Button = (Button)findViewById(R.id.button);
        Button.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){

                imageprocess_and_showResult(threshold1, threshold2);
            }
        });


        imageVIewInput.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_PICK);
                intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                intent.setType("image/*");
                startActivityForResult(intent, GET_GALLERY_IMAGE);
            }
        });


        final TextView textView1 = (TextView)findViewById(R.id.textView_threshold1);
        SeekBar seekBar1=(SeekBar)findViewById(R.id.seekBar_threshold1);
        seekBar1.setProgress(threshold1);
        seekBar1.setMax(200);
        seekBar1.setMin(0);
        seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                threshold1 = progress;
                textView1.setText(threshold1+"");
                imageprocess_and_showResult(threshold1, threshold2);

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });


        final TextView textView2 = (TextView)findViewById(R.id.textView_threshold2);
        SeekBar seekBar2=(SeekBar)findViewById(R.id.seekBar_threshold2);
        seekBar2.setProgress(threshold2);
        seekBar2.setMax(200);
        seekBar2.setMin(0);
        seekBar2.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                threshold2 = progress;
                textView2.setText(threshold2+"");
                imageprocess_and_showResult(threshold1, threshold2);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });


        if (!hasPermissions(PERMISSIONS)) { //퍼미션 허가를 했었는지 여부를 확인
            requestNecessaryPermissions(PERMISSIONS);//퍼미션 허가안되어 있다면 사용자에게 요청
        }

    }

    @Override
    protected void onResume() {
        super.onResume();

        isReady = true;
    }

    public native void imageprocessing(long inputImage, long outputImage, int th1, int th2);

    private void imageprocess_and_showResult(int th1, int th2) {

        if (isReady==false) return;

        if (img_output == null)
            img_output = new Mat();

        imageprocessing(img_input.getNativeObjAddr(), img_output.getNativeObjAddr(), th1, th2);


        Bitmap bitmapOutput = Bitmap.createBitmap(img_output.cols(), img_output.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(img_output, bitmapOutput);
        imageVIewOuput.setImageBitmap(bitmapOutput);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == GET_GALLERY_IMAGE) {


            if (data.getData() != null) {
                Uri uri = data.getData();

                try {
                    String path = getRealPathFromURI(uri);
                    int orientation = getOrientationOfImage(path); // 런타임 퍼미션 필요
                    Bitmap temp = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
                    Bitmap bitmap = getRotatedBitmap(temp, orientation);
                    imageVIewInput.setImageBitmap(bitmap);

                    img_input = new Mat();
                    Bitmap bmp32 = bitmap.copy(Bitmap.Config.ARGB_8888, true);
                    Utils.bitmapToMat(bmp32, img_input);


                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

        }
    }


    private String getRealPathFromURI(Uri contentUri) {

        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
        cursor.moveToFirst();
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

        return cursor.getString(column_index);
    }

    // 출처 - http://snowdeer.github.io/android/2016/02/02/android-image-rotation/
    public int getOrientationOfImage(String filepath) {
        ExifInterface exif = null;

        try {
            exif = new ExifInterface(filepath);
        } catch (IOException e) {
            Log.d("@@@", e.toString());
            return -1;
        }

        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);

        if (orientation != -1) {
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    return 90;

                case ExifInterface.ORIENTATION_ROTATE_180:
                    return 180;

                case ExifInterface.ORIENTATION_ROTATE_270:
                    return 270;
            }
        }

        return 0;
    }

    public Bitmap getRotatedBitmap(Bitmap bitmap, int degrees) throws Exception {
        if(bitmap == null) return null;
        if (degrees == 0) return bitmap;

        Matrix m = new Matrix();
        m.setRotate(degrees, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);

        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
    }



    // 퍼미션 코드
    static final int PERMISSION_REQUEST_CODE = 1;
    String[] PERMISSIONS  = {"android.permission.WRITE_EXTERNAL_STORAGE"};

    private boolean hasPermissions(String[] permissions) {
        int ret = 0;
        //스트링 배열에 있는 퍼미션들의 허가 상태 여부 확인
        for (String perms : permissions){
            ret = checkCallingOrSelfPermission(perms);
            if (!(ret == PackageManager.PERMISSION_GRANTED)){
                //퍼미션 허가 안된 경우
                return false;
            }

        }
        //모든 퍼미션이 허가된 경우
        return true;
    }

    private void requestNecessaryPermissions(String[] permissions) {
        //마시멜로( API 23 )이상에서 런타임 퍼미션(Runtime Permission) 요청
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissions, PERMISSION_REQUEST_CODE);
        }
    }



    @Override
    public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
        switch(permsRequestCode){

            case PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0) {
                    boolean writeAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                        if (!writeAccepted )
                        {
                            showDialogforPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다.");
                            return;
                        }
                    }
                }
                break;
        }
    }

    private void showDialogforPermission(String msg) {

        final AlertDialog.Builder myDialog = new AlertDialog.Builder(  MainActivity.this);
        myDialog.setTitle("알림");
        myDialog.setMessage(msg);
        myDialog.setCancelable(false);
        myDialog.setPositiveButton("예", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    requestPermissions(PERMISSIONS, PERMISSION_REQUEST_CODE);
                }

            }
        });
        myDialog.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                finish();
            }
        });
        myDialog.show();
    }
}




6. native-lib.cpp 파일을 다음처럼 수정합니다. 흰색은 새로 추가하고 파란색 부분은 주석처리합니다. 


#include <jni.h>
#include <opencv2/opencv.hpp>


using namespace cv;


//extern "C"
//JNIEXPORT void JNICALL
//Java_com_tistory_webnautes_useopencvwithcmake_MainActivity_ConvertRGBtoGray(JNIEnv *env,
//                                                                            jobject instance,
//                                                                            jlong matAddrInput,
//                                                                            jlong matAddrResult) {
//
//
//
//    // 입력 RGBA 이미지를 GRAY 이미지로 변환
//    Mat &matInput = *(Mat *)matAddrInput;
//    Mat &matResult = *(Mat *)matAddrResult;
//
//    cvtColor(matInput, matResult, COLOR_RGBA2GRAY);
//
//
//}



extern "C"

JNIEXPORT void JNICALL

Java_com_tistory_webnautes_useopencvwithcmake_MainActivity_imageprocessing(JNIEnv *env,

                                                                           jobject thiz,

                                                                           jlong input_image,

                                                                           jlong output_image,

                                                                           jint th1, jint th2) {




    Mat &img_input = *(Mat *) input_image;

    Mat &img_output = *(Mat *) output_image;

    

    cvtColor( img_input, img_output, COLOR_RGB2GRAY);


    blur( img_output, img_output, Size(5,5) );

    Canny( img_output, img_output, th1, th2);


}




7. 아래 첨부된 svg 이미지를 다운로드 받습니다.


image.zip



svg 이미지를 프로젝트의 drawable 폴더에 추가해야 합니다.

 

프로젝트 창에서 app을 선택하고  마우스 오른쪽 버튼을 클릭했을 때

보이는 메뉴에서 New > Vector Asset를 선택합니다.  

 

 

 

다음 항목들을 선택하고 Next 버튼을 클릭합니다.

 

Asset Type 항목에서 Local file(SVG, PSD)를 선택합니다.

Path 항목에 있는 폴더 아이콘을 클릭하여 다운로드 받아둔 SVG 이미지 파일을 선택합니다.

 

 

 

 

Finish 버튼을 클릭하면 SVG 파일이  프로젝트에 추가됩니다.

 

 


8. 이제 폰에 올려서 확인해보면 됩니다. 






반응형

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

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

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요.

  1. 이전 댓글 더보기
  2. 천천히가는중 2019.01.15 11:24

    진행을 하다가 이미지 색깔이 제대로 나오지 않아서 글을 남기게 되었습니다.
    코드는 그대로 사용을 했으며 이미지 색깔만 기존의 이미지만 다르게 나옵니다.
    그래서 카피가 잘못된건가 의심이 되서 저장되어있는 경로 file을 열어서 bitfactory를 이용해서 사용을 했을 경우에는 제대로 된 색깔로 이미지가 나오는 것을 확인했습니다.
    혹시 이유를 알수 있을까 남겨 봅니다.
    항상 감사히 보고 있습니다.

  3. 천천히가는중 2019.01.15 11:38

    네 파란색과 빨간색이 반대로 보입니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.01.15 11:41 신고


      cpp파일을 다음처럼 수정해보세요.

      //cvtColor( img_input, img_input, CV_BGR2RGB);

              cvtColor( img_input, img_output, CV_BGR2GRAY);

  4. 천천히가는중 2019.01.15 11:49

    제가 처음에 오류나서 없애두고 안된다고 하던거였네요.
    cvtColor( img_input, img_input, COLOR_BGR2RGB);
    CV_BGR2RGB를 COLOR_BGR2RGB로 바꾸고 해결했습니다. .
    빠르게 알려주셔서 감사합니다.
    항상 많은 도움 받고 있습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.01.15 11:51 신고

      아.. C API의 상수인 앞에 CV_붙은 걸로 코드가 작성되 있었군요.

      OpenCV 4.0에서 C API가 제거되어 발생하는 에러입니다.


      감사합니다 : )

  5. 작두 2019.01.18 20:16

    안녕하십니까
    혹시 유사한사진을 추출하는 것은 어떻게 하면 되겠습니까 ?

  6. 컴딱이 2019.03.25 21:16

    정말정말정말 감사합니다 제가 원하던 빠진부분없이 모든 내용들이 알차고 그저 감사할 따름입니다.
    저는 현재 자동차 번호판을 추출해서 번호판에있는 문자열 인식을 하고있는데
    안드로이드 스튜디오 환경에서 opencv는 처음하는거라 너무 막막했는데 감사합니다.

  7. DenisKIIM 2019.05.07 16:51

    선생님의 지식공유에 많은 감사를 드립니다.
    안드로이드 Edge검출 부분에서 다음 과 같은 에러가 발생 합니다.
    error: package R does not exist
    어떤 부분의 문제로 발생하는지 알고 싶습니다.
    과정은 선생님의 시킨과정대로 따라 한것 같은데 어디서 잘못했는지 잘 모르겠습니다.
    고수님의 한 수를 부탁드리겠습니다.

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

      발생한 이유가 한가지가 아닐거 같아서 맞는 솔루션인지 모르겠네요..

      메니페스트 파일에 있는 패키지 이름과
      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.tistory.webnautes.opencvimageexample">


      액티비티에 있는 패키지 명이 같은지 확인해보세요.

      package com.tistory.webnautes.opencvimageexample;



      또는 프로젝트에 속한 파일중 에러 났다고 표시를 해주는 파일이 있는지도 확인해보세요.

  8. DenisKIM 2019.05.08 14:52

    대단히 감사합니다.
    하드웨어를 전공하다보니 APP에 부조감을 많이 느낍니다.
    선생님께서 일러주신 대로 package를 처리하니 동작하였습니다.
    다시한번 감사함을 전합니다.(꾸뻑,꾸뻑)

  9. 레이프 2019.05.29 19:23

    이걸 카메라를 통해 엣지를 추출하려면 어떻게 해야하나요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.29 20:09 신고

      카메라라면 다음 포스팅보고 진행해야 할 듯합니다.

      Android NDK + OpenCV 카메라 예제 및 프로젝트 생성방법(CMake 사용)
      https://webnautes.tistory.com/1054


      카메라 영상을 둘로 분할해서 보여주는게 가능할지 모르겠네요;;;

    • 레이프 2019.05.29 20:26

      포스팅을 다 해보고 카메라 예제 생성했는데 위에 있는 소스를 어떻게 바꿔야 할지 모르겠습니다 여러가지 방법을 시도해도... 모르겠습니다

    • 레이프 2019.05.29 20:27

      그리고 카메라 영상을 둘로 분할하는것이 아니라 카메라 예제 상태에서 엣지를 추출하려고 합니다. 차선인식 어플를 개발중이라서요..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.29 20:34 신고

      cpp코드에 Canny함수만 추가하면 됩니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.29 20:50 신고

      C++로 구현된 차선인식 코드를 참고하면 어떨까 싶네요..

    • 레이프 2019.05.29 21:12

      그럼 c++로 만들어진 코드고 그걸 안드로이드 스튜디오로 가져와서 native-lib파일에 넣는건가여?? 그러면 mainactivity파일에선 어떻게 해야하나여

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.29 21:38 신고

      cpp 파일로 입력 영상이 넘어오니..

      그 영상을 가지고 처리후.

      자바코드로 결과 영상을 넘겨주거나..

      결과 좌표만 넘겨주면 됩니다..

      근데 gsl 라이브러리를 안드로이드용으로 포팅할 필요가 있어서 문제입니다.

    • 레이프 2019.06.04 02:00

      gsl말고 따른 함수는 없을까요??
      아무리 찾아봐도 gsl를 안드로이드 스튜디오에 포팅하는방법을 모르겠네요...

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

      저도 잘 모르는 부분이네요..

  10. Favicon of https://class-programming.tistory.com BlogIcon joong~ 2019.06.09 00:56 신고

    canny 말고 이미지 배경색상(필터)을 바꿔주고 싶은데....어디쪽을 봐야 될까요....? 아무리 뒤져봐도 잘 모르겠어서 댓글 드립니다...

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.09 07:07 신고

      cpp 파일의 다음 부분을 수정하세요.


      Mat &img_input = *(Mat *) inputImage;
      Mat &img_output = *(Mat *) outputImage;


      cvtColor( img_input, img_output, COLOR_RGB2GRAY);
      blur( img_output, img_output, Size(5,5) );
      Canny( img_output, img_output, th1, th2);


      img_input를 입력으로 img_out를 최종 결과물로 하면 됩니다.
      img_out에 저장된 이미지가 최종적으로 자바코드에서 보여집니다.

  11. 김서정 2019.10.01 16:54

    선생님 좋은 코드 너무감사합니다..!
    제가 할때는 잘실행이 안되는 부분들이 있어서 혹시 전체 프로젝트폴더같은것을 받고싶은데
    가능할까요..?
    혹시 github에 올려두셨거나 하신 프로젝트들이있나요..?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.01 19:01 신고

      블로그에 포스팅한 코드를 따로 보관하지는 않습니다.

  12. Favicon of https://ellie-class.tistory.com BlogIcon Ellie Lee 2019.11.09 12:52 신고

    포스팅과 동영상 잘 보고 있습니다.
    이 예제도 해봤는데, 처음에는 잘 뜨지만 프로그레스바나 버튼을 누르면 즉시 앱이 종료하네요.
    이유가 뭘까요?
    처음 뜨는 이미지를 클릭해 다른 이미지로 바꾼 다음부터는 정상적으로 작동합니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.09 15:49 신고

      처음에 영상처리할 이미지를 선택한 후 진행해야 합니다.

  13. 2020.06.24 21:17

    비밀댓글입니다

  14. 2020.06.24 21:20

    비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.24 21:23 신고

      로그캣에서 에러난 원인을 확인해야 합니다.

    • hello 2020.06.24 22:28

      어디부터 어디까지 봐야하는지를 잘 몰라서 일단 앱 종료때부터 뜨는오류 올립니다!

      2020-06-24 22:26:36.101 6015-6015/com.example.colorapp E/AndroidRuntime: FATAL EXCEPTION: main
      Process: com.example.colorapp, PID: 6015
      java.lang.NullPointerException: Attempt to invoke virtual method 'long org.opencv.core.Mat.getNativeObjAddr()' on a null object reference
      at com.example.colorapp.ColoringActivity_Camera.imageprocess_and_showResult(ColoringActivity_Camera.java:340)
      at com.example.colorapp.ColoringActivity_Camera.access$300(ColoringActivity_Camera.java:49)
      at com.example.colorapp.ColoringActivity_Camera$7.onProgressChanged(ColoringActivity_Camera.java:261)
      at android.widget.SeekBar.onProgressRefresh(SeekBar.java:98)
      at android.widget.ProgressBar.doRefreshProgress(ProgressBar.java:1555)
      at android.widget.ProgressBar.refreshProgress(ProgressBar.java:1655)
      at android.widget.ProgressBar.setProgressInternal(ProgressBar.java:1721)
      at android.widget.AbsSeekBar.trackTouchEvent(AbsSeekBar.java:1008)
      at android.widget.AbsSeekBar.onTouchEvent(AbsSeekBar.java:916)
      at android.view.View.dispatchTouchEvent(View.java:14207)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3022)
      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2745)
      at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:488)
      at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1883)
      at android.app.Activity.dispatchTouchEvent(Activity.java:4122)
      at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
      at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:446)
      at android.view.View.dispatchPointerEvent(View.java:14466)
      at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5980)
      at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5783)
      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5274)
      at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5331)
      at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5297)
      at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5449)
      at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5305)
      at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5506)
      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5278)
      at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5331)
      at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5297)
      at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5305)
      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5278)
      at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:8041)
      at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7992)
      at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7953)
      at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:8170)
      at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:219)
      at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
      2020-06-24 22:26:36.102 6015-6015/com.example.colorapp E/AndroidRuntime: at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:199)
      at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:8120)
      at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:8210)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
      at android.view.Choreographer.doCallbacks(Choreographer.java:796)
      at android.view.Choreographer.doFrame(Choreographer.java:724)
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
      at android.os.Handler.handleCallback(Handler.java:907)
      at android.os.Handler.dispatchMessage(Handler.java:99)
      at android.os.Looper.loop(Looper.java:223)
      at android.app.ActivityThread.main(ActivityThread.java:7476)
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:939)
      2020-06-24 22:26:36.446 548-698/system_process E/InputDispatcher: channel '5a7b929 com.example.colorapp/com.example.colorapp.ColoringActivity_Camera (server)' ~ Channel is unrecoverably broken and will be disposed!
      2020-06-24 22:26:37.704 1241-1241/com.google.android.gms E/BeaconBle: Missing BluetoothAdapter

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.25 19:07 신고

      Mat 객체에 대입하는 다음 2변수 이름과 함수 파라미터가 일치하나 보세요

      Mat &img_input = *(Mat *) inputImage;

         Mat &img_output = *(Mat *) outputImage;

  15. jyshin 2020.09.07 16:49

    처음에 native project로 안드로이드 프로젝트를 선택하면.. 무조건 CMakeLists.txt 파일이 생성되는건가요? Native Project로 프로젝트를 생성해도 ndk-build 방법으로 진행이 가능한지요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.07 18:40 신고

      아직 안해봐서 안된다고 단정을 지을 수는 없네요. 진행해보세요

  16. jyshin 2020.09.07 18:08

    아.. 그리고.. import android.support.annotation.NonNull; 요 부분이 회색처리 되면서 build시 에러나네요..
    원인이 멀까요..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.07 18:42 신고

      현재 androidx를 사용하기때문에 예전에 사용하던 패키지명을 바꾸어 사용해야 합니다.

      아래 링크에서 패키지이름을 검색하면 androidx에서 바뀐 패키지이름을 찾을 수 있습니다
      https://developer.android.com/jetpack/androidx/migrate/class-mappings?hl=ko

  17. jyshin 2020.09.08 14:54

    일단.. Native C++ 위저드로 프로젝트 생성하면.. CMakeLists.txt는 자동으로 생성되네요. build.gradle에도 externalNativeBuild가 cmake로 자동으로 잡히고,
    억지로 build.gradle에서 cmake 부분을 주석처리해버리고, ndkBuild로 변경한후 Android.mk, Application.mk 작성해서 빌드까지 성공해봤으나,
    에뮬레이터에서 App 실행이 안되네요. 먼가 될 듯 될 듯하면서.. 안되네요.. ㅎㅎ

    자꾸 Native C++에서 ndk-build를 실행해보려고 하는 이유가 여기 저기 가져다 쓰는 Open Source가 ndk-build로 되어 있어서 가져다 쓰기 좋기 때문입니다...
    혹시 Native C++ 위저드로 프로젝트 생성하고 ndk-build로 open CV 예제 개발해서 업로드 계획 있으신지요^^?
    날로 먹으려고 하는건 아닌지 모르겠지만.. 이런 고민하는 사람이 있을 것 같아서 문의 남깁니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.08 21:07 신고

      혹시 아래 블로그글을 보셨는지요? 이 방법도 나빠보이지 않습니다

      https://webnautes.tistory.com/923

  18. jyshin 2020.09.09 12:43

    https://webnautes.tistory.com/923 < - 요 방법을 몇주전에 따라 해서 도움을 받은 적이 있습니다.
    그러나.. empty project wizard로 프로젝트를 생성하면 native c++ 파일에 break를 걸 수가 없더군요... 즉 c++ 코드에 debugger를 쓸 수 없습니다.

    항상 Native c++ 위저드로 생성한 프로젝트만 break를 걸어서 debugger를 돌릴 수 있더라구요...
    그래서 제가 Native C++ 위저드로 ndk-build를 시도해보려는 겁니다..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.09 22:33 신고

      각각 별도의 빌드 방식이여서 같이 사용하는 건 불가능할 듯합니다.

      검색해도 안나오네요.

      ndk-build 방식으로 된 프로젝트를 cmake 방식으로 변환하여 진행해보세요..

      cmake와 ndk-build 방식의 차이는 빌드 관련 파일만 차이가 있기때문에 변환이 가능할 듯합니다.

  19. jyshin 2020.09.14 19:27

    Native C++ 프로젝트를 예를 들어 STAR.aar로 빌드해서.. 다른 프로젝트에 import한 후 STAR.aar에 있는 STAR.cpp 파일의 sum_two_digits( )이라는 C 함수를 호출할 수 있는지요?
    시도해보는데.. 호출이 안되서 질문드리는겁니다.. 먼가 제가 이해를 잘 못하고 있는건지...

  20. Favicon of https://learn-and-give.tistory.com BlogIcon 바른생활머시마 2020.09.25 14:17 신고

    감사합니다~ 정말 잘 봤어요. PC에서 하던 것을 android에서 해보려고 하는데 쉽지 않네요. 좋은 길잡이가 되어 주셔서 감사합니다.
    음..... 그런데... 왼쪽의 카테고리 보니까 dlib+android도 있던데...혹시 계획 있으신지..^^a....

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.25 20:02 신고

      이미 있습니다.
      카데고리가 옮겨져 있었네요.

      DLib를 사용하여 Android에서 얼굴 검출(Face Landmarks Detection)하는 예제
      https://webnautes.tistory.com/1229

  21. kannu 2020.10.11 08:24

    혹시 안드로이드에서 opencv는 흑백사진만 이미지 필터링이 가능한가요?

    bilateral 처리 방법을 찾아봐도 다 gray로 바꾸고 시작하더라구요..

+ Recent posts