11

I'm trying to translate the following Python function, that applies a mask to an image, into Java:

# Applies an image mask.
def region_of_interest(img, vertices):
    #defining a blank mask to start with
    mask = np.zeros_like(img)   

    #defining a 3 channel or 1 channel color to fill the mask with depending on the input image
    if len(img.shape) > 2:
        channel_count = img.shape[2]  # i.e. 3 or 4 depending on your image
        ignore_mask_color = (255,) * channel_count
    else:
        ignore_mask_color = 255

    #filling pixels inside the polygon defined by "vertices" with the fill color    
    cv2.fillPoly(mask, vertices, ignore_mask_color)

    #returning the image only where mask pixels are nonzero
    masked_image = cv2.bitwise_and(img, mask)
    return masked_image

So far, this is what I've got:

public static opencv_core.Mat applyMask(opencv_core.Mat image, opencv_core.MatVector vertices) {
  opencv_core.Mat mask = opencv_core.Mat.zeros(image.size(), opencv_core.CV_8U).asMat();

  opencv_core.Scalar color = new opencv_core.Scalar(image.channels()); // 3
  double[] colors = new double[] {
    255.0, 255.0, 255.0, 255.0,
    255.0, 255.0, 255.0, 255.0,
    255.0, 255.0, 255.0, 255.0};
  color.put(colors, 0, colors.length);

  opencv_imgproc.fillPoly(mask, vertices, color);

  opencv_core.Mat dst = new opencv_core.Mat();
  opencv_core.bitwise_and(image, mask, dst);
  return dst;
}

But, it isn't working. When I try invoking this method like in the following example:

opencv_core.MatVector points = new opencv_core.MatVector(
  new opencv_core.Mat(2, 3, opencv_core.CV_32F, new IntPointer(1, 2, 3, 4, 5, 6))
);

opencv_core.MatVector vertices = new opencv_core.MatVector(points);
opencv_core.Mat masked = LaneManager.applyMask(src, vertices);

(I'm assuming this is the right way to build a 2x3 matrix of three points with two coordinates each (1,2), (3, 4) and (5,6))

I get an exception:

java.lang.RuntimeException: std::bad_alloc
at org.bytedeco.javacpp.opencv_imgproc.fillPoly(Native Method)

I'm using OpenCV as provided by org.bytedeco.javacpp-presets:opencv-platform:3.2.0-1.3 via Maven Central.

I must admit that I'm at a loss here: What's the idiomatic Java way of doing the same thing as the Python function above?

serv-inc
  • 35,772
  • 9
  • 166
  • 188
zugaldia
  • 702
  • 3
  • 8
  • I think what you are looking for is related with this other stackoverflow post http://stackoverflow.com/questions/221830/set-bufferedimage-alpha-mask-in-java. Hope this can help. – Francisco Valle Mar 28 '17 at 17:04
  • How are you initializing the src variable you're sending into applyMask? – Daedalus Mar 31 '17 at 03:01
  • One suspicious thing is that you are apparently defining a color for a 3-channel image with 12 color components. That can't be right. – Dave Mar 31 '17 at 14:30
  • @Daedalus I'm simply invoking `opencv_core.Mat src = opencv_imgcodecs.imread(filename);` where `filename` is a `String` with the file path. – zugaldia Mar 31 '17 at 15:39
  • @Dave Replacing the color with `opencv_core.Scalar color = new opencv_core.Scalar(255, 255, 255, 0);` doesn't work either, and gives the same error message. – zugaldia Mar 31 '17 at 15:40

2 Answers2

2

Alright, I finally figured it out. If you define your coordinates with:

int[] points = new int[] {x1, y1, x2, y2, ...};

Then you can simply apply a mask with the following code:

opencv_core.Mat mask = new opencv_core.Mat(image.size(), image.type());

// Array of polygons where each polygon is represented as an array of points
opencv_core.Point polygon = new opencv_core.Point();
polygon.put(points, 0, points.length);
opencv_imgproc.fillPoly(mask, polygon, new int[] {points.length / 2}, 1, new opencv_core.Scalar(255, 255, 255, 0));

opencv_core.Mat masked = new opencv_core.Mat(image.size(), image.type());
opencv_core.bitwise_and(image, mask, masked);

Where image is the original image, and masked is the masked result.

The problem was that the original list of points wasn't defined properly.

zugaldia
  • 702
  • 3
  • 8
1

Maybe some whiz has the complete answers. Here is food for thought:

  1. The Java API is a direct copy of the CPP API: http://opencv.org/opencv-java-api.html
  2. The error std::bad_alloc occurs when you fail to allocate required storage space. (http://answers.opencv.org/question/28959/cascade-training-killed-and-bad_alloc/)

  3. There are two CPP methods:
  4. You don't need to convert from Mat to InputArray, but you can (and should) just pass a Mat object where an InputArray is requested (https://stackoverflow.com/a/32976883/1587329)

Community
  • 1
  • 1
serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • Thank you for looking into this, but this isn't helping. None of the `fillPoly` methods exposed on the Java side take an `InputArray`. They either take a `Mat` or an `UMat`. I think my issue is more with not defining properly the list of points. For example, it'd be helpful if you could give me an example of how to create the points argument with, say, a list of 4 points, with 2 coordinates each. – zugaldia Mar 31 '17 at 15:46
  • @zugaldia: Thank you for the feedback. Have you tried memory options to increase the heap size, as in f.ex. `-Xmx512m`? – serv-inc Mar 31 '17 at 16:02
  • Yes. I think they issue is I want to use the method `public static native void fillPoly(Mat blackImg, MatVector points, Scalar whiteColor);` but I'm not defining `MatVector points` properly. How would I create a new `MatVector` for this use case? – zugaldia Mar 31 '17 at 16:07