コード例 #1
0
ファイル: CaptureImage.java プロジェクト: bmalla6920/Chequers
  /** Capture images and run color processing through here */
  public void capture() {
    VideoCapture camera = new VideoCapture();

    camera.set(12, -20); // change contrast, might not be necessary

    // CaptureImage image = new CaptureImage();

    camera.open(0); // Useless
    if (!camera.isOpened()) {
      System.out.println("Camera Error");

      // Determine whether to use System.exit(0) or return

    } else {
      System.out.println("Camera OK");
    }

    boolean success = camera.read(capturedFrame);
    if (success) {
      try {
        processWithContours(capturedFrame, processedFrame);
      } catch (Exception e) {
        System.out.println(e);
      }
      // image.processFrame(capturedFrame, processedFrame);
      // processedFrame should be CV_8UC3

      // image.findCaptured(processedFrame);

      // image.determineKings(capturedFrame);

      int bufferSize = processedFrame.channels() * processedFrame.cols() * processedFrame.rows();
      byte[] b = new byte[bufferSize];

      processedFrame.get(0, 0, b); // get all the pixels
      // This might need to be BufferedImage.TYPE_INT_ARGB
      img =
          new BufferedImage(
              processedFrame.cols(), processedFrame.rows(), BufferedImage.TYPE_INT_RGB);
      int width = (int) camera.get(Highgui.CV_CAP_PROP_FRAME_WIDTH);
      int height = (int) camera.get(Highgui.CV_CAP_PROP_FRAME_HEIGHT);
      // img.getRaster().setDataElements(0, 0, width, height, b);
      byte[] a = new byte[bufferSize];
      System.arraycopy(b, 0, a, 0, bufferSize);

      Highgui.imwrite("camera.jpg", processedFrame);
      System.out.println("Success");
    } else System.out.println("Unable to capture image");

    camera.release();
  }
コード例 #2
0
  public void copyMat(Mat src, Mat dest) {
    int srcRows = src.rows();
    int srcCols = src.cols();
    int destRows = dest.rows();
    int destCols = dest.cols();

    for (int i = 0; i < srcRows; i++) {
      for (int j = 0; j < srcCols; j++) {
        double bit = src.get(i, j)[0];
        dest.put(i, j, bit);
        System.out.println(bit);
      }
    }
  }
コード例 #3
0
  public void templateMatching() {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    int match_method = 5;
    int max_Trackbar = 5;
    Mat data = Highgui.imread("images/training_data/1" + "/data (" + 1 + ").jpg");
    Mat temp = Highgui.imread("images/template.jpg");
    Mat img = data.clone();

    int result_cols = img.cols() - temp.cols() + 1;
    int result_rows = img.rows() - temp.rows() + 1;
    Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);

    Imgproc.matchTemplate(img, temp, result, match_method);
    Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());

    double minVal;
    double maxVal;
    Point minLoc;
    Point maxLoc;
    Point matchLoc;
    // minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
    Core.MinMaxLocResult res = Core.minMaxLoc(result);

    if (match_method == Imgproc.TM_SQDIFF || match_method == Imgproc.TM_SQDIFF_NORMED) {
      matchLoc = res.minLoc;
    } else {
      matchLoc = res.maxLoc;
    }

    // / Show me what you got
    Core.rectangle(
        img,
        matchLoc,
        new Point(matchLoc.x + temp.cols(), matchLoc.y + temp.rows()),
        new Scalar(0, 255, 0));

    // Save the visualized detection.
    Highgui.imwrite("images/samp.jpg", img);
  }
コード例 #4
0
ファイル: CaptureImage.java プロジェクト: bmalla6920/Chequers
  /**
   * Determines where captured pieces are
   *
   * @param in Mat image of the board
   */
  public void findCaptured(Mat in) {
    int vsegment = in.rows() / 8; // only accounts 8 playable
    int hsegment = in.cols() / 12; // 8 playable, 2 capture, 2 extra
    int offset; // offset for playable board

    int capSquares = 12; // number of capture squares
    int rowNum = 1; // starting row number for capture squares
    int rightdx = 48;
    int leftdx = 0;
    offset = hsegment;
    int count = 0;
    // keep track of captured squares
    // left: end user, right: system
    for (int i = 0; i < capSquares; i++) {
      // find where roi should be
      Point p1 =
          new Point(
              offset + count * hsegment, rowNum * vsegment); // top left point of rectangle (x,y)
      Point p2 =
          new Point(
              offset + (count + 1) * hsegment,
              (rowNum + 1) * vsegment); // bottom right point of rectangle (x,y)
      // create rectangle that is board square
      Rect bound = new Rect(p1, p2);

      char color;

      // frame only includes rectangle
      Mat roi = new Mat(in, bound);

      // get the color
      color = identifyColor(roi);

      switch (color) {
        case COLOR_BLUE:
          // Imgproc.rectangle(in, p1, p2, new Scalar(255, 0, 0), 3);
          Core.rectangle(in, p1, p2, new Scalar(255, 0, 0), 2);
          captured[i] = 1;
          break;
        case COLOR_ORANGE:
          // Imgproc.rectangle(in, p1, p2, new Scalar(0, 128, 255), 3);
          Core.rectangle(in, p1, p2, new Scalar(0, 128, 255), 2);
          captured[i] = 1;
          break;
        case COLOR_WHITE:
          // Imgproc.rectangle(in, p1, p2, new Scalar(255, 255, 255), 3);
          Core.rectangle(in, p1, p2, new Scalar(255, 255, 255), 2);
          captured[i] = 0;
          break;
        case COLOR_BLACK:
          // Imgproc.rectangle(in, p1, p2, new Scalar(0, 0, 0), 3);
          Core.rectangle(in, p1, p2, new Scalar(255, 255, 255), 2);
          captured[i] = 0;
          break;
      }

      count++;
      if (count == 1) {
        offset = hsegment * 10 - rightdx;
      } else if (count == 2) {
        count = 0;
        rightdx -= 6;
        leftdx += 6;
        offset = hsegment - leftdx;
        rowNum++;
      }
    }
  }
コード例 #5
0
ファイル: CaptureImage.java プロジェクト: bmalla6920/Chequers
  /**
   * Identifies the color in the frame
   *
   * @param in the Mat image in the region of interest
   * @return the color
   */
  public char identifyColor(Mat in) {
    // Mat blue = new Mat(in.rows(), in.cols(), CvType.CV_8UC1);
    // Mat green = new Mat(in.rows(), in.cols(), CvType.CV_8UC1);
    // Mat red = new Mat(in.rows(), in.cols(), CvType.CV_8UC1);

    // split the channels of the image
    Mat blue = new Mat(); // default is CV_8UC3
    Mat green = new Mat();
    Mat red = new Mat();
    List<Mat> channels = new ArrayList<Mat>(3);
    Core.split(in, channels);
    blue = channels.get(0); // makes all 3 CV_8UC1
    green = channels.get(1);
    red = channels.get(2);
    // System.out.println(blue.toString());

    // add the intensities
    Mat intensity = new Mat(in.rows(), in.cols(), CvType.CV_32F);
    // Mat mask = new Mat();
    Core.add(blue, green, intensity); // , mask, CvType.CV_32F);
    Core.add(intensity, red, intensity); // , mask, CvType.CV_32F);

    // not sure if correct from here to ...

    Mat inten = new Mat();
    Core.divide(intensity, Scalar.all(3.0), inten);
    // System.out.println(intensity.toString());
    // Core.divide(3.0, intensity, inten);
    // if intensity = intensity / 3.0; means element-wise division
    // use intensity.muls(Mat m)
    // so make new Mat m of same size that has each element of 1/3

    /*
    * or
    * About per-element division you can use Core.divide()

    Core.divide(A,Scalar.all(d), B);

    It's equivalent to B=A/d
    */

    // find normalized values
    Mat bnorm = new Mat();
    Mat gnorm = new Mat();
    Mat rnorm = new Mat();
    // blue.convertTo(blue, CvType.CV_32F);
    // green.convertTo(green, CvType.CV_32F);
    // red.convertTo(red, CvType.CV_32F);

    Core.divide(blue, inten, bnorm);
    Core.divide(green, inten, gnorm);
    Core.divide(red, inten, rnorm);

    // find average norm values
    Scalar val = new Scalar(0);
    val = Core.mean(bnorm);
    String value[] = val.toString().split(",");
    String s = value[0].substring(1);
    double bavg = Double.parseDouble(s);
    val = Core.mean(gnorm);
    String value1[] = val.toString().split(",");
    String s1 = value1[0].substring(1);
    double gavg = Double.parseDouble(s1);
    val = Core.mean(rnorm);
    String value2[] = val.toString().split(",");
    String s2 = value2[0].substring(1);
    double ravg = Double.parseDouble(s2);

    // ... here

    // original values
    /*
    // define the reference color values
    //double RED[] = {0.4, 0.5, 1.8};
    //double GREEN[] = {1.0, 1.2, 1.0};
    double BLUE[] = {1.75, 1.0, 0.5};
    //double YELLOW[] = {0.82, 1.7, 1.7};
    double ORANGE[] = {0.2, 1.0, 2.0};
    double WHITE[] = {2.0, 1.7, 1.7};
    //double BLACK[] = {0.0, 0.3, 0.3};
    */

    // define the reference color values
    // double RED[] = {0.4, 0.5, 1.8};
    // double GREEN[] = {1.0, 1.2, 1.0};
    double BLUE[] = {1.75, 1.0, 0.5};
    // double YELLOW[] = {0.82, 1.7, 1.7};
    double ORANGE[] = {0.2, 1.0, 2.0};
    double WHITE[] = {2.0, 1.7, 1.7};
    // double BLACK[] = {0.0, 0.3, 0.3};

    // compute the square error relative to the reference color values
    // double minError = 3.0;
    double minError = 2.0;
    double errorSqr;
    char bestFit = 'x';

    // test++;
    // System.out.print("\n\n" + test + "\n\n");

    // check BLUE fitness
    errorSqr = normSqr(BLUE[0], BLUE[1], BLUE[2], bavg, gavg, ravg);
    System.out.println("Blue: " + errorSqr);
    if (errorSqr < minError) {
      minError = errorSqr;
      bestFit = COLOR_BLUE;
    }
    // check ORANGE fitness
    errorSqr = normSqr(ORANGE[0], ORANGE[1], ORANGE[2], bavg, gavg, ravg);
    System.out.println("Orange: " + errorSqr);
    if (errorSqr < minError) {
      minError = errorSqr;
      bestFit = COLOR_ORANGE;
    }
    // check WHITE fitness
    errorSqr = normSqr(WHITE[0], WHITE[1], WHITE[2], bavg, gavg, ravg);
    System.out.println("White: " + errorSqr);
    if (errorSqr < minError) {
      minError = errorSqr;
      bestFit = COLOR_WHITE;
    }
    // check BLACK fitness
    /*errorSqr = normSqr(BLACK[0], BLACK[1], BLACK[2], bavg, gavg, ravg);
    System.out.println("Black: " + errorSqr);
    if(errorSqr < minError)
    {
    	minError = errorSqr;
    	bestFit = COLOR_BLACK;
    }*/

    // return the best fit color label
    return bestFit;
  }
コード例 #6
0
ファイル: CaptureImage.java プロジェクト: bmalla6920/Chequers
  public void processWithContours(Mat in, Mat out) {
    int playSquares = 32; // number of playable game board squares

    // keep track of starting row square
    int parity = 0; // 0 is even, 1 is odd, tied to row number
    int count = 0; // row square
    int rowNum = 0; // row number, starting at 0

    int vsegment = in.rows() / 8; // only accounts 8 playable
    int hsegment = in.cols() / 10; // 8 playable, 2 capture
    int hOffset = hsegment * 2; // offset for playable board
    int vOffset = vsegment + 40;

    // For angle of camera
    int dx = 80;
    int ddx = 0;
    hsegment -= 16;

    int dy = 20;
    vsegment -= 24;
    int ddy = 0;

    // Go through all playable squares
    for (int i = 0; i < playSquares; i++) {
      // change offset depending on the row
      if (parity == 0) // playable squares start on 2nd square from left
      {
        if (rowNum >= 5) dx -= 3;
        hOffset = hsegment * 2 + dx;
      } else // playable squares start on immediate left
      {
        if (rowNum >= 5) dx -= 3;
        hOffset = hsegment + dx;
      }

      if (rowNum == 0) ddy = 5;
      if (rowNum == 4) if (count == 6) ddx = 10;
      if (rowNum == 5) {
        if (count == 0) ddx = -6;
        else if (count == 2) ddx = 6;
        else if (count == 4) ddx = 12;
        else if (count == 6) ddx = 20;
      }
      if (rowNum == 6) {
        if (count == 0) ddx = 0;
        else if (count == 2) ddx = 16;
        else if (count == 4) ddx = 32;
        else if (count == 6) ddx = 40;
      }
      if (rowNum == 7) {
        if (count == 0) ddx = 6;
        else if (count == 2) ddx = 24;
        else if (count == 4) ddx = 40;
        else ddx = 52;
      }

      // find where roi should be
      // System.out.println("" + vOffset);
      Point p1 =
          new Point(
              hOffset + count * hsegment + ddx + 5,
              vOffset + rowNum * vsegment - dy - 5 - ddy); // top left point of rectangle (x,y)
      Point p2 =
          new Point(
              hOffset + (count + 1) * hsegment + ddx - 5,
              vOffset
                  + (rowNum + 1) * vsegment
                  - dy
                  - 5
                  - ddy); // bottom right point of rectangle (x,y)

      // create rectangle that is board square
      Rect bound = new Rect(p1, p2);

      Mat roi;
      char color;
      if (i == 0) {
        // frame only includes rectangle
        roi = new Mat(in, bound);

        // get the color
        color = identifyColor(roi);

        // copy input image to output image
        in.copyTo(out);
      } else {
        // frame only includes rectangle
        roi = new Mat(out, bound);

        // get the color
        color = identifyColor(roi);
      }

      Imgproc.cvtColor(roi, roi, Imgproc.COLOR_BGR2GRAY); // change to single color

      Mat canny = new Mat();
      Imgproc.Canny(roi, canny, 20, 40); // make image a canny image that is only edges; 2,4
      // lower threshold values find more edges
      List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
      Mat hierarchy = new Mat(); // holds nested contour information
      Imgproc.findContours(
          canny,
          contours,
          hierarchy,
          Imgproc.RETR_LIST,
          Imgproc.CHAIN_APPROX_SIMPLE); // Imgproc.RETR_LIST, TREE

      System.out.println(++test + "\t" + contours.size());

      if (contours.size() > 3) // or error value for color is below 1
      {
        switch (color) {
          case COLOR_BLUE:
            // Imgproc.rectangle(out, p1, p2, new Scalar(255, 0, 0), 2);
            Core.rectangle(out, p1, p2, new Scalar(255, 0, 0), 2);
            board[i] = CheckersBoard.BLACK; // end user's piece
            break;
          case COLOR_ORANGE:
            // Imgproc.rectangle(out, p1, p2, new Scalar(0, 128, 255), 2);
            Core.rectangle(out, p1, p2, new Scalar(0, 128, 255), 2);
            board[i] = CheckersBoard.WHITE; // system's piece
            break;
          case COLOR_WHITE:
            // Imgproc.rectangle(out, p1, p2, new Scalar(255, 255, 255), 2);
            Core.rectangle(out, p1, p2, new Scalar(255, 255, 255), 2);
            board[i] = CheckersBoard.EMPTY;
            break;
          case COLOR_BLACK: // this is black
            // Imgproc.rectangle(out, p1, p2, new Scalar(0, 0, 0), 2);
            Core.rectangle(
                out,
                p1,
                p2,
                new Scalar(0, 0, 0),
                2); // maybe add 8, 0 as line type and fractional bits
            board[i] = CheckersBoard.EMPTY;
            break;
        }
      }

      System.out.println("in color switch " + board[i]);
      count += 2;
      if (count == 8) {
        parity = ++parity % 2; // change odd or even
        count = 0;
        rowNum++;
        hsegment += 2;
        dx -= 10;
        dy += 10;
        vsegment += 3;
        ddy = 0;
      }
    }
  }
コード例 #7
0
ファイル: CaptureImage.java プロジェクト: bmalla6920/Chequers
  /**
   * Determines which pieces are kings
   *
   * @param in Mat image of board
   */
  public void determineKings(Mat in) {
    int playSquares = 32;

    Mat dst = new Mat(in.rows(), in.cols(), in.type());
    in.copyTo(dst);

    Imgproc.cvtColor(dst, dst, Imgproc.COLOR_BGR2GRAY); // change to single color

    Mat canny = new Mat();
    Imgproc.Canny(dst, canny, 100, 200); // make image a canny image that is only edges; 2,4
    // lower threshold values find more edges
    List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Mat hierarchy = new Mat(); // holds nested contour information
    Imgproc.findContours(
        canny,
        contours,
        hierarchy,
        Imgproc.RETR_LIST,
        Imgproc.CHAIN_APPROX_SIMPLE); // Imgproc.RETR_LIST, TREE

    // draw contour image
    Mat mask = new Mat();
    mask = Mat.zeros(dst.size(), dst.type());
    Imgproc.drawContours(
        mask, contours, -1, new Scalar(255, 255, 255), 1, 8, hierarchy, 2, new Point());
    Highgui.imwrite("contours.jpg", mask);

    ArrayList occupied = new ArrayList<Integer>();
    for (int i = 0; i < playSquares; i++) {
      if (board[i] != 0) occupied.add(i);
    }

    for (int i = 0; i < contours.size(); i++) // assuming only contours are checker pieces
    {
      // determine if it should be a king
      // use Rect r = Imgproc.boundingRect then find height of it by r.height

      // Get bounding rect of contour
      Rect bound = Imgproc.boundingRect(contours.get(i));

      if (bound.height > in.rows() / 8) {
        // board[(int) occupied.get(0)]++; // make it a king
        // occupied.remove(0);
      }
    }

    // or apply to each region of interest

    /*
    // keep track of starting row square
    int parity = 0; // 0 is even, 1 is odd, tied to row number
    int count = 0; // row square
    int rowNum = 0; // row number, starting at 0

    int vsegment = in.rows() / 8; // only accounts 8 playable
    int hsegment = in.cols() / 12; // 8 playable, 2 capture, 2 extra
    int offset = hsegment * 2; // offset for playable board

    // For angle of camera
    int dx = 48;
    hsegment -= 8;


    // Go through all playable squares
    for (int i = 0; i < playSquares; i++)
    {
    	// change offset depending on the row
    	if (parity == 0) // playable squares start on immediate left
    		offset = hsegment * 3 + dx;
    	else // playable squares start on 2nd square from left
    		offset = hsegment * 2 + dx;

    	// find where roi should be
    	Point p1 = new Point(offset + count * hsegment, rowNum * vsegment); // top left point of rectangle (x,y)
    	Point p2 = new Point(offset + (count + 1) * hsegment, (rowNum + 1) * vsegment); // bottom right point of rectangle (x,y)

    	// create rectangle that is board square
    	Rect bound = new Rect(p1, p2);

    	// frame only includes rectangle
    	Mat roi = new Mat(in, bound);

           Imgproc.cvtColor(roi, roi, Imgproc.COLOR_BGR2GRAY); // change to single color

           Mat canny = new Mat();
           Imgproc.Canny(roi, canny, 2, 4); // make image a canny image that is only edges; 2,4
           // lower threshold values find more edges
           List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
           Mat hierarchy = new Mat(); // holds nested contour information
           Imgproc.findContours(canny, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); // Imgproc.RETR_LIST, TREE

           // Get bounding rect of contour
              Rect rect = Imgproc.boundingRect(contours.get(0));

              if (rect.height > in.rows() / 8)
    	{
    		board[i]++; // make it a king
    	}

    	count += 2;
    	if (count == 8)
    	{
    		parity = ++parity % 2; // change odd or even
    		count = 0;
    		rowNum++;
    		hsegment += 1;
    		dx -= 6;
    	}
    }*/
  }
コード例 #8
0
ファイル: CaptureImage.java プロジェクト: bmalla6920/Chequers
  /**
   * Processes the board image
   *
   * @param in image captured of board
   * @param out processed image of board
   */
  public void processFrame(Mat in, Mat out) {
    // multiple regions of interest

    int playSquares = 32; // number of playable game board squares

    // keep track of starting row square
    int parity = 0; // 0 is even, 1 is odd, tied to row number
    int count = 0; // row square
    int rowNum = 0; // row number, starting at 0

    int vsegment = in.rows() / 8; // only accounts 8 playable
    int hsegment = in.cols() / 10; // 8 playable, 2 capture
    int hOffset = hsegment * 2; // offset for playable board
    int vOffset = vsegment + 40;

    // For angle of camera
    int dx = 80;
    int ddx = 0;
    hsegment -= 16;

    int dy = 20;
    vsegment -= 24;

    // Go through all playable squares
    for (int i = 0; i < playSquares; i++) {
      // change offset depending on the row
      if (parity == 0) // playable squares start on 2nd square from left
      {
        if (rowNum >= 5) dx -= 3;
        hOffset = hsegment * 2 + dx;
      } else // playable squares start on immediate left
      {
        if (rowNum >= 5) dx -= 3;
        hOffset = hsegment + dx;
      }

      if (rowNum == 4) if (count == 6) ddx = 10;
      if (rowNum == 5) {
        if (count == 0) ddx = -6;
        else if (count == 2) ddx = 6;
        else if (count == 4) ddx = 12;
        else if (count == 6) ddx = 20;
      }
      if (rowNum == 6) {
        if (count == 0) ddx = 0;
        else if (count == 2) ddx = 16;
        else if (count == 4) ddx = 32;
        else if (count == 6) ddx = 40;
      }
      if (rowNum == 7) {
        if (count == 0) ddx = 0;
        else if (count == 2) ddx = 24;
        else if (count == 4) ddx = 40;
        else ddx = 52;
      }

      // find where roi should be
      // System.out.println("" + vOffset);
      Point p1 =
          new Point(
              hOffset + count * hsegment + ddx,
              vOffset + rowNum * vsegment - dy); // top left point of rectangle (x,y)
      Point p2 =
          new Point(
              hOffset + (count + 1) * hsegment + ddx,
              vOffset + (rowNum + 1) * vsegment - dy); // bottom right point of rectangle (x,y)

      // create rectangle that is board square
      Rect bound = new Rect(p1, p2);

      char color;
      if (i == 0) {
        // frame only includes rectangle
        Mat roi = new Mat(in, bound);

        // get the color
        color = identifyColor(roi);

        // copy input image to output image
        in.copyTo(out);
      } else {
        // frame only includes rectangle
        Mat roi = new Mat(out, bound);

        // get the color
        color = identifyColor(roi);
      }

      // annotate the output image
      // scalar values as (blue, green, red)
      switch (color) {
        case COLOR_BLUE:
          // Imgproc.rectangle(out, p1, p2, new Scalar(255, 0, 0), 2);
          Core.rectangle(out, p1, p2, new Scalar(255, 0, 0), 2);
          board[i] = CheckersBoard.BLACK; // end user's piece
          break;
        case COLOR_ORANGE:
          // Imgproc.rectangle(out, p1, p2, new Scalar(0, 128, 255), 2);
          Core.rectangle(out, p1, p2, new Scalar(0, 128, 255), 2);
          board[i] = CheckersBoard.WHITE; // system's piece
          break;
        case COLOR_WHITE:
          // Imgproc.rectangle(out, p1, p2, new Scalar(255, 255, 255), 2);
          Core.rectangle(out, p1, p2, new Scalar(255, 255, 255), 2);
          board[i] = CheckersBoard.EMPTY;
          break;
        case COLOR_BLACK: // this is black
          // Imgproc.rectangle(out, p1, p2, new Scalar(0, 0, 0), 2);
          Core.rectangle(
              out,
              p1,
              p2,
              new Scalar(0, 0, 0),
              2); // maybe add 8, 0 as line type and fractional bits
          board[i] = CheckersBoard.EMPTY;
          break;
      }

      count += 2;
      if (count == 8) {
        parity = ++parity % 2; // change odd or even
        count = 0;
        rowNum++;
        hsegment += 2;
        dx -= 10;
        dy += 10;
        vsegment += 3;
      }
    }
  }
コード例 #9
0
    public void map(Text key, Text value, Context context)
        throws InterruptedException, IOException {

      String filename = key.toString();
      String json = value.toString();

      // Make sure the input is valid
      if (!(filename.isEmpty() || json.isEmpty())) {

        // Change the json-type feature to Mat-type feature
        Mat descriptor = json2mat(json);
        if (descriptor != null) {
          // Read the query feature from the cache in Hadoop
          Mat query_features;
          String pathStr = context.getConfiguration().get("featureFilePath");
          FileSystem fs = FileSystem.get(context.getConfiguration());
          FSDataInputStream fsDataInputStream = fs.open(new Path(pathStr));
          StringBuilder sb = new StringBuilder();

          // Use a buffer to read the query_feature
          int remain = fsDataInputStream.available();
          while (remain > 0) {
            int read;
            byte[] buf = new byte[BUF_SIZE];
            read = fsDataInputStream.read(buf, fsDataInputStream.available() - remain, BUF_SIZE);
            sb.append(new String(buf, 0, read, StandardCharsets.UTF_8));
            remain = remain - read;
            System.out.println("remain:" + remain + "\tread:" + read + "\tsb.size:" + sb.length());
          }

          // Read the query_feature line by line
          //                    Scanner sc = new Scanner(fsDataInputStream, "UTF-8");
          //                    StringBuilder sb = new StringBuilder();
          //                    while (sc.hasNextLine()) {
          //                        sb.append(sc.nextLine());
          //                    }
          //                    String query_json = sb.toString();
          //                    String query_json = new String(buf, StandardCharsets.UTF_8);

          String query_json = sb.toString();
          fsDataInputStream.close();
          query_features = json2mat(query_json);

          // Get the similarity of the current database image against the query image
          DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
          MatOfDMatch matches = new MatOfDMatch();

          // Ensure the two features have same length of cols (the feature extracted are all 128
          // cols(at least in this case))
          if (query_features.cols() == descriptor.cols()) {

            matcher.match(query_features, descriptor, matches);
            DMatch[] dMatches = matches.toArray();

            // Calculate the max/min distances
            //                    double max_dist = Double.MAX_VALUE;
            //                    double min_dist = Double.MIN_VALUE;
            double max_dist = 0;
            double min_dist = 100;
            for (int i = 0; i < dMatches.length; i++) {
              double dist = dMatches[i].distance;
              if (min_dist > dist) min_dist = dist;
              if (max_dist < dist) max_dist = dist;
            }
            // Only distances ≤ threshold are good matches
            double threshold = max_dist * THRESHOLD_FACTOR;
            //                    double threshold = min_dist * 2;
            LinkedList<DMatch> goodMatches = new LinkedList<DMatch>();

            for (int i = 0; i < dMatches.length; i++) {
              if (dMatches[i].distance <= threshold) {
                goodMatches.addLast(dMatches[i]);
              }
            }

            // Get the ratio of good_matches to all_matches
            double ratio = (double) goodMatches.size() / (double) dMatches.length;

            System.out.println("*** current_record_filename:" + filename + " ***");
            System.out.println("feature:" + descriptor + "\nquery_feature:" + query_features);
            System.out.println(
                "min_dist of keypoints:" + min_dist + "  max_dist of keypoints:" + max_dist);
            System.out.println(
                "total_matches:" + dMatches.length + "\tgood_matches:" + goodMatches.size());
            //                    System.out.println("type:" + descriptor.type() + " channels:" +
            // descriptor.channels() + " rows:" + descriptor.rows() + " cols:" + descriptor.cols());
            //                    System.out.println("qtype:" + query_features.type() + "
            // qchannels:" + query_features.channels() + " qrows:" + query_features.rows() + "
            // qcols:" + query_features.cols());
            System.out.println();

            if (ratio > PERCENTAGE_THRESHOLD) {
              // Key:1        Value:filename|ratio
              context.write(ONE, new Text(filename + "|" + ratio));
              //                        context.write(ONE, new Text(filename + "|" +
              // String.valueOf(goodMatches.size())));
            }
          } else {
            System.out.println("The size of the features are not equal");
          }
        } else {
          // a null pointer, do nothing
          System.out.println("A broken/null feature:" + filename);
          System.out.println();
        }
      }
    }