Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Translate Python function to apply mask to image into Java

Tags:

java

opencv

mask

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?

like image 926
zugaldia Avatar asked Mar 22 '17 01:03

zugaldia


People also ask

How do you invert a mask in Python?

We then perform Erosion, Morphing, and Dilation techniques on the image to create the mask. Now, to invert the mask, we use bitwise_not the method of cv2 library to flip the pixel values (0 ->1 and 1 ->0). Finally, we display this inverted masked image.

How do you make a mask from a picture?

Open an image editor that supports layers. Open the photo to which you want to apply a mask. Save the file with a new name as a duplicate so that you do not accidentally overwrite the original. Create a new layer and paint it solid white, and adjust the layer transparency so that you can see the underlying image.

What is CV mask?

Masking is a common technique to extract the Region of Interest (ROI). In openCV, it is possible to construct arbitrary masking shape using draw function and bitwise operation.


2 Answers

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:
    • C++: void fillPoly(Mat& img, const Point** pts, const int* npts, int ncontours, const Scalar& color, int lineType=LINE_8, int shift=0, Point offset=Point() ), and
    • C++: void fillPoly(InputOutputArray img, InputArrayOfArrays pts, const Scalar& color, int lineType=LINE_8, int shift=0, Point offset=Point() )
  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)

like image 25
serv-inc Avatar answered Oct 22 '22 20:10

serv-inc


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.

like image 125
zugaldia Avatar answered Oct 22 '22 19:10

zugaldia