ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android NDK + OpenCV 이미지 로드하여 영상처리하는 예제
    OpenCV/Android 개발 환경 및 예제 2019. 5. 7. 21:09



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

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



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







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





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



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





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





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





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





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

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





    업데이트  

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

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




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



    Android 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으로 수정합니다.

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


    apply plugin: 'com.android.application'

    android {
       compileSdkVersion 28
       defaultConfig {
           applicationId "com.tistory.webnautes.opencvndkimageexample"
           minSdkVersion 26
           targetSdkVersion 28
           versionCode 1
           versionName "1.0"
           testInstrumentationRunner "android.support.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 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 android.support.annotation.NonNull;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.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) {

           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 instance,
                                                                              jlong inputImage,
                                                                              jlong outputImage,

                                                                              jint th1,

                                                                              jint th2) {



       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);


    }




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


    image.zip




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

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

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

     

     

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

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

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

    Size 항목에 있는 Override를 체크합니다.

     

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





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




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

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

    유튜브 구독하기


    댓글 115

    • 이전 댓글 더보기
    • 익명 2017.05.19 01:13


      자료 감사드립니다 혹시 그러면 8개의 파일을 한꺼번에 불러와서 이미지를 하나의 이미지를 합치려고하는데 어떻게 하면 가능할까요...

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.05.19 08:35 신고


        OpenCV에 이미지 합치는 함수가 있습니다. 아래 포스팅은 c++로 한건데 java도 같은 이름의 함수가 있을 겁니다.

        http://webnautes.tistory.com/1119

    • 익명 2017.05.19 16:40


      이미지 불러오는거는 그러면 위에있는 함수를 여러번 사용하면 될까요? 자바는 처음이라 좀 모르는부분이많네요.

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.05.20 08:27 신고


        한번 사용하면 2개의 이미지가 1개로 됩니다..
        다시 합쳐진 이미지를 입력으로 하는걸 반복하면 될거 같군요..

        c++ 코드로 잘되는지 확인하고 안드로이드에서 진행하는게 좋을듯합니다.

        해당 함수를 여러번 사용하셔도 됩니다..

    • 익명 2017.05.20 23:03


      감사합니다. 파일 8장을 native.cpp에서 작업하여 붙여 출력하는거는 완료했습니다. 그런데 이번에는 저장하는게 문제가되네요. 인터넷에 돌아다니는 ImageView 저장방법을 사용하여보앗는데 저장이안되고 오류가나서 앱이 꺼지더군요. 혹시 저장하는 방법을 알 수 잇을까요?

    • 익명 2017.05.28 23:49


      저장하는것 해결했습니다 8장이미지를 불러와서 이어붙인뒤 해결했는데 위에 imageprocess_and_showResult() 함수끝에 핸드폰(안드로이드) 내부경로를 이용하여 파일명을 설정하여 저장하였습니다. 문제는 카메라로 에를들어 많은 장수에 사진을 찍고 (16장 또는 32장) 그파일을 native.cpp에서 작업하면 프로그램이 뻗는 증상(멈췃다 다시켜집니다.)이 발생하는데 해결방법이없을까요..

      해상도가 너무커서 스마트폰이 버티지 못하는건지 Opencv와 호환이 안되는건지 잘모르겠습니다...

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.05.29 15:00 신고


        이미지 크기를 카메라에서 찍은 영상 해상도 이내로 조정해야 할 듯합니다...

        cpp 코드에서 사이즈 조정후 자바로 다시 보낼때 해상도 문제로 뻗는 경우가 종종 있었습니다.

    • 익명 2017.05.29 00:27


      카메라 예제는 참고로 블로그에 있는 http://webnautes.tistory.com/822 이걸사용하여 만들었습니다. 저걸 사용해서 만드니 카메라 자체의 Full 해상도로 찍던데 따로 설정하는 방법과 가로로 고정시키는 방법이 있나요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.05.29 22:30 신고


        해당 코드에서 이 부분을 수정하면 되지 않을까 싶네요..

        w와 h값을 바꾸면 될텐데..
        아마 웹캠처럼 지원하는 해상도가 미리
        정해져있을 듯합니다.

        PictureCallback jpegCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {

        //이미지의 너비와 높이 결정
        int w = camera.getParameters().getPictureSize().width;
        int h = camera.getParameters().getPictureSize().height;

    • opencv초보 2017.06.07 21:54


      안녕하세요. 항상 선생님 블로그에서 도움얻어가는 초보자입니다. 항상 감사하며 염치불구하고 질문 하나만 올리겠습니다.
      이미지 변환어플을 구현중에 있는데요. 갤러리 이미지를 불러와서 버튼을 누르면 제가 코딩한대로 색이 바뀌는 그런어플입니다.
      일단 구현을해서 변환까진 됐는데, 크기가 3배로 확대된 상태로 뿌려지네요. 축소시키려고 손가락으로 밀어도 때면 다시 돌아오구요.
      궁여지책으로 bitmapInput 사이즈를 1/3하는 방식 해보니 크기는 딱 맞는데 엄청나게 계단현상이 생기내요 ㅠㅠ..
      비트맵 뿌려주는곳에서 문제인지... c++코드에서 문제인지 도통 알수가 없네요.. 혹시 왜 이런문제가 생기는지 알고계시나요??

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.06.08 00:18 신고


        될진 모르겠지만...

        스마트폰 화면 크기로 흰색 이미지를 만들고..

        1/3으로 줄인 이미지를 복사해서 흰색 이미지 중앙에 뿌려보세요..

    • 금영환 2017.07.12 14:12


      안녕하세요
      이번에 안드로이드 자체를 처음시작하여
      이미 openCV는 사용해본 경험이 있는 쌩 초보자입니다..

      다름이 아니라 두개의 이미지를 불러와서
      두개의 이미지 빼서 결과를 얻어내고 싶은데요

      두 이미지를 빼는 함수를 못찾고 있습니다.. 계속 뒤져보고 있는데
      혹시 두 이미지 빼기 함수가 없는 건지,, 아니면 제가 못찾고있는건지

      한번 도와주시면 감사드릴게요!

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


        원본 영상에 새로운 물체가 나타나면 검출하는 걸 찾으시는것 같네요..

        http://docs.opencv.org/3.2.0/d1/dc5/tutorial_background_subtraction.html

    • abc 2018.07.30 16:36


      07-30 16:27:31.029 24460-24460/? E/Zygote: v2
      07-30 16:27:31.030 24460-24460/? E/Zygote: accessInfo : 0
      07-30 16:27:31.316 24460-24460/com.hou.smhrc09.ex_opencv1 E/cv::error(): OpenCV(3.4.2) Error: Assertion failed (VScn::contains(scn) && VDcn::contains(dcn) && VDepth::contains(depth)) in cv::CvtHelper<VScn, VDcn, VDepth, sizePolicy>::CvtHelper(cv::InputArray, cv::OutputArray, int) [with VScn = cv::Set<3, 4>; VDcn = cv::Set<3, 4>; VDepth = cv::Set<0, 2, 5>; cv::SizePolicy sizePolicy = (cv::SizePolicy)2u; cv::InputArray = const cv::_InputArray&; cv::OutputArray = const cv::_OutputArray&], file /build/3_4_pack-android/opencv/modules/imgproc/src/color.hpp, line 253
      07-30 16:27:31.318 24460-24460/com.hou.smhrc09.ex_opencv1 A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 24460 (rc09.ex_opencv1)

      코드 작성후 실행시켰을 때 앱 사용 계속 중단되고
      위와 같은 에러가 발생하는데 혹시 무슨 이유인지 알 수 있을까요?

    • 학생 2018.11.21 23:14


      이게 무슨 오류일까요.. 문법상 오류는 없는데 빌드 오류가 나네요.,. ㅠㅠㅠ

      Build command failed.
      Error while executing process C:\Users\USER\AppData\Local\Android\Sdk\cmake\3.6.4111459\bin\cmake.exe with arguments {--build C:\Users\USER\AndroidStudioProjects\OpenOpen2\app\.externalNativeBuild\cmake\debug\x86_64 --target native-lib}
      [1/2] Building CXX object CMakeFiles/native-lib.dir/src/main/cpp/native-lib.cpp.o
      [2/2] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libnative-lib.so
      FAILED: cmd.exe /C "cd . && C:\Users\USER\AppData\Local\Android\Sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=x86_64-none-linux-android21 --gcc-toolchain=C:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/toolchains/x86_64-4.9/prebuilt/windows-x86_64 --sysroot=C:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/sysroot -fPIC -isystem C:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/sysroot/usr/include/x86_64-linux-android -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -std=c++11 -frtti -fexceptions -std=gnu++11 -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -nostdlib++ --sysroot C:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/platforms/android-21/arch-x86_64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -LC:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64 -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libnative-lib.so CMakeFiles/native-lib.dir/src/main/cpp/native-lib.cpp.o C:/Users/USER/AndroidStudioProjects/OpenOpen/app/src/main/jniLibs/x86_64/libopencv_java3.so -llog -latomic -lm "C:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_static.a" "C:/Users/USER/AppData/Local/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++abi.a" && cd ."
      C:\Users\USER\AndroidStudioProjects\OpenOpen2\app\src\main\cpp/native-lib.cpp:25: error: undefined reference to 'cv::imread(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, int)'
      clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
      ninja: build stopped: subcommand failed.

    • 천천히가는중 2019.01.15 11:24


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

    • 천천히가는중 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);

    • 천천히가는중 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가 제거되어 발생하는 에러입니다.


        감사합니다 : )

    • 작두 2019.01.18 20:16


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

    • 컴딱이 2019.03.25 21:16


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

    • 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;



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

    • DenisKIM 2019.05.08 14:52


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

    • 레이프 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 신고


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

    • 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에 저장된 이미지가 최종적으로 자바코드에서 보여집니다.

    • 김서정 2019.10.01 16:54


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

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


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

Designed by Tistory.