How to Convert Python Code to Java without numpy

Viewed 1063

I have a method in Python that makes use of OpenCV to remove the background from an image. I want the same functionality to work with android's version of OpenCV but I just cant seem to wrap my head around how the arrays work and how I can process them.

This is what I have so far in Java :

private Bitmap GetForeground(Bitmap source){
        source = scale(source,300,300);
        Mat mask = Mat.zeros(source.getHeight(),source.getWidth(),CvType.CV_8U);
        Mat bgModel = Mat.zeros(1,65,CvType.CV_64F);
        Mat ftModel = Mat.zeros(1,65,CvType.CV_64F);
        int x = (int)Math.round(source.getWidth()*0.1);
        int y = (int)Math.round(source.getHeight()*0.1);
        int width = (int)Math.round(source.getWidth()*0.8);
        int height = (int)Math.round(source.getHeight()*0.8);
        Rect rect = new Rect(x,y, width,height);
        Mat sourceMat = new Mat();
        Utils.bitmapToMat(source, sourceMat);
        Imgproc.grabCut(sourceMat, mask, rect, bgModel, ftModel, 5, Imgproc.GC_INIT_WITH_RECT);

        int frameSize=sourceMat.rows()*sourceMat.cols();
        byte[] buffer= new byte[frameSize];
        mask.get(0,0,buffer);
        for (int i = 0; i < frameSize; i++) {
            if (buffer[i] == 2 || buffer[i] == 0){
                buffer[i] = 0;
            }else{
                buffer[i] = 1 ;
            }
        }

        byte[][] sourceArray = getMultiChannelArray(sourceMat);
        byte[][][] reshapedMask = ReshapeArray(buffer, sourceMat.rows(), sourceMat.cols());
        return source;
    }

    private byte[][][] ReshapeArray(byte[] arr, int rows, int cols){
        byte[][][] out = new byte[cols][rows][1];
        int index=0;

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                out[i][j][0] = arr[index];
                index++;
            }
        }
        return out;
    }

    public static byte[][] getMultiChannelArray(Mat m) {
        //first index is pixel, second index is channel
        int numChannels=m.channels();//is 3 for 8UC3 (e.g. RGB)
        int frameSize=m.rows()*m.cols();
        byte[] byteBuffer= new byte[frameSize*numChannels];
        m.get(0,0,byteBuffer);

        //write to separate R,G,B arrays
        byte[][] out=new byte[frameSize][numChannels];
        for (int p=0,i = 0; p < frameSize; p++) {
            for (int n = 0; n < numChannels; n++,i++) {
                out[p][n]=byteBuffer[i];
            }
        }
        return out;
    }

The python code I want to recreate :

image = cv2.imread('Images/handheld.jpg')
image = imutils.resize(image, height = 300)
mask = np.zeros(image.shape[:2],np.uint8)
bgModel = np.zeros((1,65),np.float64)
frModel = np.zeros((1,65),np.float64)
height, width, d = np.array(image).shape
rect = (int(width*0.1),int(height*0.1),int(width*0.8),int(height*0.8))
cv2.grabCut(image, mask, rect, bgModel,frModel, 5,cv2.GC_INIT_WITH_RECT)
mask = np.where((mask==2) | (mask == 0),0,1).astype('uint8')
image = image*mask[:,:,np.newaxis]

I have no idea how to convert the last two lines of the python code. If there is a way to just run python clean on an android device within my own project that would also be awesome.

2 Answers

Let's see both the commands and try to convert them to Java API calls. It may not be simple 2 line in code.

mask = np.where((mask==2) | (mask == 0),0,1).astype('uint8')

In the above command, we are creating a new image mask which has uint data type of pixel values. The new mask matrix would have value 0 for every position where previous mask has a value of either 2 or 0, otherwise 1. Let's demonstrate this with an example:

mask = [
[0, 1, 1, 2],
[1, 0, 1, 3],
[0, 1, 1, 2],
[2, 3, 1, 0],
]

After this operation the output would be:

mask = [
[0, 1, 1, 0],
[1, 0, 1, 1],
[0, 1, 1, 0],
[0, 1, 1, 0],
]

So this above command is simply generating a binary mask with only 0 and 1 values. This can replicated in Java using Core.compare() method as:

// Get a mask for all `1` values in matrix.
Mat mask1vals;
Core.compare(mask, new Scalar(1), mask1vals, Core.CMP_EQ);

// Get a mask for all `3` values in matrix.
Mat mask3vals;
Core.compare(mask, new Scalar(3), mask3vals, Core.CMP_EQ);

// Create a combined mask
Mat foregroundMask;
Core.max(mask1vals, mask3vals, foregroundMask)

Now you need to multiply this foreground mask with the input image, to get final grabcut image as:

// First convert the single channel mat to 3 channel mat
Imgproc.cvtColor(foregroundMask, foregroundMask, Imgproc.COLOR_GRAY2BGR);
// Now simply take min operation
Mat out;
Core.min(foregroundMask, image, out);
Related