title: JNI UnsatisfiedLinkError - Android
body: <p>I'm a beginner in jni and I'm trying to load a library but I keep getting UnSatisfiedLinkError in the Log. I've checked multiple times all of my files but still the same error.</p>
  
  <p>Android.mk</p>
  
  <pre><code>LOCAL_PATH := $(call my-dir)
  
  include $(CLEAR_VARS)
  
  
  LOCAL_MODULE    := native_sample
  LOCAL_SRC_FILES := myFirstApp.cpp
  LOCAL_LDLIBS +=  -llog -ldl
  
  include $(BUILD_SHARED_LIBRARY)
  </code></pre>
  
  <p>myFirstApp.cpp</p>
  
  <pre><code>#include &lt;jni.h&gt;
  #include &lt;opencv2/core/core.hpp&gt;
  #include "opencv2/highgui/highgui.hpp"
  #include &lt;vector&gt;
  #include &lt;math.h&gt;
  #include &lt;android/log.h&gt;
  #include &lt;opencv2/features2d/features2d.hpp&gt;
  #include &lt;opencv2/legacy/legacy.hpp&gt;
  #include "opencv2/video/tracking.hpp"
  #include &lt;time.h&gt;
  #include &lt;math.h&gt;
  #include &lt;string.h&gt;
  
  #define APPNAME "myFirstApp"
  using namespace std;
  using namespace cv;
  
  extern "C" {
  
  static int numTemplate = 24;
  static int tWidth = 256;
  static int tHight = 256;
  static vector&lt;Mat&gt; tmplts;
  static vector&lt;vector&lt;KeyPoint&gt; &gt; keyPointsTmplts;
  static vector&lt;Mat&gt; descriptorsTmplts;
  static vector&lt;Mat&gt; trainDescriptors;
  // find squares vars
  static vector&lt;vector&lt;Point&gt; &gt; squares;
  static vector&lt;Point2f&gt; squaresCenters;
  static vector&lt;int&gt; squaresAbsAreas;
  static vector&lt;int&gt; clustersAreas;
  static double scaleFactor = 1.5;
  static double MARKER_RATIO = 0.03;
  //clustering vars
  static vector&lt;Point2f&gt; clusterCenters;
  static vector&lt;vector&lt;Point&gt; &gt; clusterBoundaries;
  static int CLUSTERTHRESHOLD = 25;
  //tracking variables
  static Mat prevFrame;
  static vector&lt;Point2f&gt; oldPoints;
  static TermCriteria termcrit(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03);
  static Size winSize(31, 31);
  static Size subPixWinSize(10, 10);
  //Recognition
  static std::vector&lt;int&gt; matchingResults;
  static vector&lt;Mat&gt; hos;
  static Mat warpedImg;
  static Mat cropped;
  static vector&lt;vector&lt;KeyPoint&gt; &gt; candidatesKeypoints;
  vector&lt;Mat&gt; candidates;
  
  // DETECTION
  static FastFeatureDetector detector(16);
  //static int MAX_KEYPOINTS_COUNT=100;
  //static GoodFeaturesToTrackDetector detector(MAX_KEYPOINTS_COUNT, 0.01, 10, 3, false, 0.04);
  
  // DESCRIPTOR
  static FREAK extractor;
  
  // MATCHER
  static BFMatcher matcher(NORM_HAMMING, true);
  
  
  double diffclock(clock_t clock1, clock_t clock2);
  
  
  
  // for int arrays
  static Mat points2Mat(const Point* p) {
      Mat srcP(4, 2, CV_32FC1);
      for (int i = 0; i &lt; 4; i++) {
          srcP.at&lt;float&gt;(i, 0) = p[i].x;
          srcP.at&lt;float&gt;(i, 1) = p[i].y;
      }
      return srcP;
  }
  
  // for float arrays
  static Mat points2MatF(const Point2f* p) {
      Mat srcP(4, 2, CV_32FC1);
      for (int i = 0; i &lt; 4; i++) {
          srcP.at&lt;float&gt;(i, 0) = p[i].x;
          srcP.at&lt;float&gt;(i, 1) = p[i].y;
      }
      return srcP;
  }
  
  static Mat prepareWarpDstMat(const Point* p) {
      Mat dstP = cvCreateMat(4, 2, CV_32FC1);
      dstP.at&lt;float&gt;(0, 0) = p[0].x;
      dstP.at&lt;float&gt;(0, 1) = p[0].y;
      dstP.at&lt;float&gt;(1, 0) = p[0].x + tWidth;
      dstP.at&lt;float&gt;(1, 1) = p[0].y;
      dstP.at&lt;float&gt;(2, 0) = p[0].x + tWidth;
      dstP.at&lt;float&gt;(2, 1) = p[0].y + tHight;
      dstP.at&lt;float&gt;(3, 0) = p[0].x;
      dstP.at&lt;float&gt;(3, 1) = p[0].y + tHight;
      return dstP;
  }
  
  //-----------------------------Find Squares-------------------------------------------
  
  // helper function:
  // finds a cosine of angle between vectors
  // from pt0-&gt;pt1 and from pt0-&gt;pt2
  static double angle(Point pt1, Point pt2, Point pt0) {
      double dx1 = pt1.x - pt0.x;
      double dy1 = pt1.y - pt0.y;
      double dx2 = pt2.x - pt0.x;
      double dy2 = pt2.y - pt0.y;
      return (dx1 * dx2 + dy1 * dy2)
              / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
  }
  
  static void clearVectors() {
      // clear all vectors from data
      squares.clear();
      matchingResults.clear();
      squaresCenters.clear();
      squaresAbsAreas.clear();
      clusterCenters.clear();
      clusterBoundaries.clear();
      clustersAreas.clear();
      candidatesKeypoints.clear();
      candidates.clear();
  
  }
  
  // the sequence is stored in the specified memory storage
  static void findSquares(const Mat&amp; grayImg) {
      clock_t begin = clock();
      Mat timg, gray, scaledImg;
  
      resize(grayImg, scaledImg, Size(0, 0), 1 / scaleFactor, 1 / scaleFactor,
              CV_INTER_CUBIC);
  
      __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "scaledImage %i %i",
              scaledImg.cols,scaledImg.rows);
      // Gaussian blurring better than pyr up and down
      GaussianBlur(scaledImg, timg, Size(5, 5), 0, 0, BORDER_DEFAULT); // t11
  
      vector&lt;vector&lt;Point&gt; &gt; contours;
      // find squares in every color plane of the image
  
      Canny(timg, gray, 50, 200, 5); //t3
  
      dilate(gray, gray, Mat(), Point(-1, -1));
  
      // find contours and store them all as a list
      findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
  
      vector&lt;Point&gt; approx;
  
      // test each contour
      for (int i = 0; i &lt; contours.size(); i++) {
          // approximate contour with accuracy proportional
          // to the contour perimeter
          approxPolyDP(Mat(contours[i]), approx,
                  arcLength(Mat(contours[i]), true) * 0.02, true);
  
          // square contours should have 4 vertices after approximation
          // relatively large area (to filter out noisy contours)
          // and be convex.
          // Note: absolute value of an area is used because
          // area may be positive or negative - in accordance with the
          // contour orientation
          int imgArea = gray.cols * gray.rows;
          int absArea = fabs(contourArea(Mat(approx)));
          if (approx.size() == 4 &amp;&amp; absArea &gt; 1000 &amp;&amp; isContourConvex(Mat(approx))
                  &amp;&amp; absArea &lt; 0.8 * imgArea) {
              double maxCosine = 0;
              Point a, b, c, d;
  
              for (int j = 2; j &lt; 5; j++) {
                  // find the maximum cosine of the angle between joint edges
                  a = approx[j % 4];
                  b = approx[j - 2];
                  c = approx[j - 1];
                  double cosine = fabs(angle(a, b, c));
                  maxCosine = MAX(maxCosine, cosine);
              }
  
              // if cosines of all angles are small
              // (all angles are ~90 degree) then write quandrange
              // vertices to resultant sequence
              if (maxCosine &lt; 0.3) {
  
                  // restore scaling
                  Point* p0 = (Point*) &amp;approx[0];
                  Point* p1 = (Point*) &amp;approx[1];
                  Point* p2 = (Point*) &amp;approx[2];
                  Point* p3 = (Point*) &amp;approx[3];
  
                  p0-&gt;x = p0-&gt;x * scaleFactor;
                  p0-&gt;y = p0-&gt;y * scaleFactor;
                  p1-&gt;x = p1-&gt;x * scaleFactor;
                  p1-&gt;y = p1-&gt;y * scaleFactor;
                  p2-&gt;x = p2-&gt;x * scaleFactor;
                  p2-&gt;y = p2-&gt;y * scaleFactor;
                  p3-&gt;x = p3-&gt;x * scaleFactor;
                  p3-&gt;y = p3-&gt;y * scaleFactor;
  
                  Point2f center = (*p0 + *p1 + *p2 + *p3) * (0.25);
                  //  //__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "squareCenter %G,%G",center.x,center.y);
                  squares.push_back(approx);
                  squaresCenters.push_back(center);
                  squaresAbsAreas.push_back(absArea);
              }
  
          }
      }
  
  }
  
  //--------------------------------Cluster Rectangles-------------------------------------
  
  static void updateCluster(int pNum, int* clusters, int n) {
      for (int i = 0; i &lt; n; i++) {
          if (clusters[pNum] != clusters[i]) {
              Point2f p0 = (Point2f) squaresCenters[pNum];
              Point2f p1 = (Point2f) squaresCenters[i];
  //          //__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "norm %G",
  //                  norm(p0-p1));
              if (norm(p0 - p1) &lt; CLUSTERTHRESHOLD) {
                  clusters[i] = clusters[pNum];
                  updateCluster(i, clusters, n);
              }
          }
      }
  
  }
  static int TRACKED_THRESHOLD = 100;
  static bool inTrackingList(Point2f clusterCenter) {
  //  int tracklistSize = trackedMarkersCenters.size();
  //  Point2f trackedCenter;
  //  for (int i = 0; i &lt; tracklistSize; i++) {
  //      trackedCenter = (Point2f) trackedMarkersCenters[i][0];
  ////        //__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "dist %G",
  ////                norm(clusterCenter - trackedCenter));
  //      if (norm(clusterCenter - trackedCenter) &lt; TRACKED_THRESHOLD)
  //          return true;
  //  }
      return false;
  }
  
  
  
  static void mergeRectangles() {
      int n = squaresCenters.size();
      int clusters[n];
      int clusterCounter = 0;
  
      for (int i = 0; i &lt; n; i++)
          clusters[i] = -1;
      for (int i = 0; i &lt; n; i++)
          if (clusters[i] == -1) {
              clusters[i] = clusterCounter;
              clusterCounter++;
              updateCluster(i, clusters, n);
          }
      //__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "clusters %i",
      //clusterCounter);
      int members, maxArea;
  
      for (int i = 0; i &lt; clusterCounter; i++) {
          members = 0;
          Point2f center(0, 0);
          maxArea = -1;
          vector&lt;Point&gt; maxSquare;
          for (int j = 0; j &lt; n; j++) {
              if (clusters[j] == i) {
                  center += (Point2f) squaresCenters[j];
                  members++;
  
                  if (maxArea &lt; (int) squaresAbsAreas[j]) {
                      maxArea = (int) squaresAbsAreas[j];
                      maxSquare = squares[j];
                  }
              }
          }
          center *= (1.0 / members);
  
          if (!inTrackingList(center)) {
              clusterCenters.push_back(center);
              clusterBoundaries.push_back(maxSquare);
              clustersAreas.push_back(maxArea);
              matchingResults.push_back(-3);
              const Point* floPoin = &amp;maxSquare[0];
              Mat scene = points2Mat(floPoin);
          }
  
      }
  
  }
  
  //------------------------------Process Filtered squares--------------------------------------
  
  static int imageArea;
  // crop squares
  static void cropAndWarpCandidate(Mat&amp; grayImg, const Point* p, int i) {
  
      //__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Area %i %i %G %G", i,
      //      clustersAreas[i], imageArea, clustersAreas[i] * 1.0 / imageArea);
  //
  //  if (clustersAreas[i] &lt; MARKER_RATIO * imageArea) {
  //
  //      matchingResults[i] = -2;
  //      return;
  //  }
  
      Mat srcPMat = points2Mat(p);
      Mat dstPMat = prepareWarpDstMat(p);
      Mat ho = findHomography(srcPMat, dstPMat, 0);
  
  
  
      warpPerspective(grayImg, warpedImg, ho,
              Size(grayImg.cols + tWidth, grayImg.rows + tHight));
  
      cropped = Mat(warpedImg, Rect(p[0].x, p[0].y, tWidth, tHight));
  
  //  int templateIndex = matchCandidate(cropped);
  //
  //  matchingResults[i] = templateIndex;
      candidates.push_back(cropped);
  
      srcPMat.release();
      dstPMat.release();
      warpedImg.release();
      cropped.release();
  
  }
  
  // the function draws all the squares in the image
  static void processFilteredSquares(Mat&amp; grayImg) {
      imageArea = grayImg.cols * grayImg.rows;
  
      int squaresSize = clusterBoundaries.size();
  
      //__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "filtered %d",
      //      squaresSize);
      for (int i = 0; i &lt; squaresSize; i++) {
          const Point* p = &amp;clusterBoundaries[i][0];
          cropAndWarpCandidate(grayImg, p, i);
  
      }
  
  }
  
  //------------------Drawing ---------------------------------------
  
  
  static void drawFilteredSquaresWithoutMarker(Mat&amp; rgbImg) {
      int squaresSize = clusterBoundaries.size();
      __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "squares %i",
              squaresSize);
      int n = 4;
      for (int i = 0; i &lt; squaresSize; i++) {
          const Point* p = &amp;clusterBoundaries[i][0];
          Point center = clusterCenters[i];
          polylines(rgbImg, &amp;p, &amp;n, 1, true, Scalar(0, 255, 0, 255), 3, CV_AA);
          circle(rgbImg, center, 10, Scalar(0, 255, 0, 255));
      }
  
  }
  
  
  // calc time helper
  double diffclock(clock_t clock1, clock_t clock2) {
      double diffticks = clock1 - clock2;
      double diffms = (diffticks * 1000) / CLOCKS_PER_SEC;
      return diffms;
  }
  
  JNIEXPORT jint JNICALL Java_com_example_myfirstapp_RegisterMarkerMain_findMarkersNative(
          JNIEnv* env, jobject, jlong addrRgba) {
      //clock_t begin = clock();
      Mat&amp; mRgb = *(Mat*) addrRgba;
      Mat mgray(mRgb.rows, mRgb.cols, CV_8UC1);
      cvtColor(mRgb, mgray, CV_RGBA2GRAY, 1); // the working one
  
      clearVectors();
  
      findSquares(mgray);
      mergeRectangles();
  
      processFilteredSquares(mgray);
  
      drawFilteredSquaresWithoutMarker(mRgb);
      __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Candidates %i",candidates.size());
      return clusterBoundaries.size();
  //  clock_t end = clock();
  
  //  mgray.release();
  }
  
  JNIEXPORT void JNICALL Java_com_example_myfirstapp_RegisterMarkerMain_loadCand(
          JNIEnv* env, jobject, jlong addrRgba, jlong descriptorAdd, jint i) {
      vector&lt;KeyPoint&gt; keyPoints;
      Mat nativeM = candidates[i];
      Mat&amp; mRgb = *(Mat*) addrRgba;
      Mat&amp; descriptor = *(Mat*) descriptorAdd;
      nativeM.copyTo(mRgb);
  
      Mat descriptorUnFiltered;
      detector.detect(nativeM, keyPoints);
      if(keyPoints.size()==0)
          return;
      extractor.compute(nativeM, keyPoints, descriptorUnFiltered);
      vector&lt;vector&lt;DMatch&gt; &gt; matches;
      if(descriptorUnFiltered.rows==0)
          return;
      matcher.radiusMatch(descriptorUnFiltered, descriptorUnFiltered, matches,
              50);
  
  
  
      descriptor = descriptorUnFiltered.row(0);
  
      std::vector&lt;DMatch&gt; mat;
      for (int j = 1; j &lt; matches.size(); j++) {
  
          mat = matches[j];
          // if no matches neglect
          if (mat.size() &gt;= 2) {
              DMatch m = mat[1];
  
              if (m.trainIdx &lt; m.queryIdx)
                  continue;
  
              else
                  vconcat(descriptor, descriptorUnFiltered.row(m.queryIdx),
                          descriptor);
  
          } else {
              DMatch m0 = mat[0];
              vconcat(descriptor, descriptorUnFiltered.row(m0.queryIdx),
                      descriptor);
  
          }
      }
  
  }
  
  }
  </code></pre>
  
  <p>That's where I call loadLibrary in the activity</p>
  
  <pre><code>private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
          @Override
          public void onManagerConnected(int status) {
              Log.i("loading libs", "OpenCV loading status " + status);
              switch (status) {
              case LoaderCallbackInterface.SUCCESS: {
                  Log.i("loading libs", "OpenCV loaded successfully");
  
                  // Load native library after(!) OpenCV initialization
                  System.loadLibrary("native_sample");
  
              }
                  break;
              default: {
                  super.onManagerConnected(status);
              }
                  break;
              }
          }
      };
  </code></pre>
  
  <p>Any help would be really appreciated.</p>