1

I am developing an image segmentation application that will use watersheds. For that, I found a code that I will need to use in python. However, I'm having a hard time converting to kotlin, as the Mat Mat () doesn't have the zero_likes function, just the zero function. I am using opencv 3.31. How do I do this check in kotlin:

marked[marked == 1] = 0
marked[marked > 1] = 255

Code python:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Load the image
img = cv2.imread("/path/to/image.png", 3)

# Create a blank image of zeros (same dimension as img)
# It should be grayscale (1 color channel)
marker = np.zeros_like(img[:,:,0]).astype(np.int32)

# This step is manual. The goal is to find the points
# which create the result we want. I suggest using a
# tool to get the pixel coordinates.

# Dictate the background and set the markers to 1
marker[204][95] = 1
marker[240][137] = 1
marker[245][444] = 1
marker[260][427] = 1
marker[257][378] = 1
marker[217][466] = 1

# Dictate the area of interest
# I used different values for each part of the car (for visibility)
marker[235][370] = 255    # car body
marker[135][294] = 64     # rooftop
marker[190][454] = 64     # rear light
marker[167][458] = 64     # rear wing
marker[205][103] = 128    # front bumper

# rear bumper
marker[225][456] = 128
marker[224][461] = 128
marker[216][461] = 128

# front wheel
marker[225][189] = 192
marker[240][147] = 192

# rear wheel
marker[258][409] = 192
marker[257][391] = 192
marker[254][421] = 192

# Now we have set the markers, we use the watershed
# algorithm to generate a marked image
marked = cv2.watershed(img, marker)

# Plot this one. If it does what we want, proceed;
# otherwise edit your markers and repeat
plt.imshow(marked, cmap='gray')
plt.show()

# Make the background black, and what we want to keep white
marked[marked == 1] = 0
marked[marked > 1] = 255

# Use a kernel to dilate the image, to not lose any detail on the outline
# I used a kernel of 3x3 pixels
kernel = np.ones((3,3),np.uint8)
dilation = cv2.dilate(marked.astype(np.float32), kernel, iterations = 1)

# Plot again to check whether the dilation is according to our needs
# If not, repeat by using a smaller/bigger kernel, or more/less iterations
plt.imshow(dilation, cmap='gray')
plt.show()

# Now apply the mask we created on the initial image
final_img = cv2.bitwise_and(img, img, mask=dilation.astype(np.uint8))

# cv2.imread reads the image as BGR, but matplotlib uses RGB
# BGR to RGB so we can plot the image with accurate colors
b, g, r = cv2.split(final_img)
final_img = cv2.merge([r, g, b])

# Plot the final result
plt.imshow(final_img)
plt.show()

code kotlin:

 // Load the image
   val srcOriginal = Imgcodecs.imread(currentPhotoPath)

    // Create a blank image of zeros (same dimension as img)
    // It should be grayscale (1 color channel)
    val markers = Mat.zeros(srcOriginal.rows(), srcOriginal.cols(), CvType.CV_32S)

    // This step is manual. The goal is to find the points
    // which create the result we want. I suggest using a
    // tool to get the pixel coordinates.

    // Dictate the area of interest
    for(x in my_canvas.pointsToDrawX.indices) {
        for(y in my_canvas.pointsToDrawY.indices) {
            markers.put(
                my_canvas.pointsToDrawX.get(x).toInt(),
                my_canvas.pointsToDrawY.get(y).toInt(),
                255.0
            )
        }
    }
    //Now we have set the markers, we use the watershed
    //algorithm to generate a marked image
    Imgproc.watershed(srcOriginal, markers)
    val marker_tempo = Mat()
    markers.convertTo(marker_tempo, CvType.CV_8U)
    // Plot this one. If it does what we want, proceed;
    // otherwise edit your markers and repeat
    //Create Bitmap
    val bmpOut = Bitmap.createBitmap(srcOriginal.cols(), srcOriginal.rows(), Bitmap.Config.RGB_565)
    Utils.matToBitmap(marker_tempo, bmpOut)
    val mPath = Environment.getExternalStorageDirectory().toString() + "/gray.png"
    Imgcodecs.imwrite(mPath,marker_tempo)
    //Make the background black, and what we want to keep white


    //Use a kernel to dilate the image, to not lose any detail on the outline
    //I used a kernel of 3x3 pixels
    val kernel = Mat(3, 3, CvType.CV_8U)
    val dilatation = Imgproc.dilate(marker_tempo, marker_tempo, kernel)
    val mPath1 = Environment.getExternalStorageDirectory().toString() + "/dilation.png"
    Imgcodecs.imwrite(mPath1,marker_tempo)
    //Now apply the mask we created on the initial image
    val final_image = Core.bitwise_and(srcOriginal, srcOriginal, dilatation)
    //cv2.imread reads the image as BGR, but matplotlib uses RGB
    //BGR to RGB so we can plot the image with accurate colors

In pointsToDrawX and pointsToDrawY I'm saving all the x, y coordinates of the user's touch event on the screen. It is from these coordinates that I will pass to the watershed algorithm to perform the segmentation and remove the background from the image. Can someone help me convert this code?

Tecnologia da Net
  • 215
  • 3
  • 7
  • 23

2 Answers2

2
//Load the image
srcOriginal = Imgcodecs.imread(currentPhotoPath)

//Create a blank image of zeros (same dimension as img)
//It should be grayscale (1 color channel)
markers = Mat.zeros(srcOriginal.rows(), srcOriginal.cols(), CvType.CV_32S)

//This step is manual. The goal is to find the points
//which create the result we want. I suggest using a
//tool to get the pixel coordinates.
//Dictate the background and set the markers to 1
for (value in 0..my_canvas.pointsToDrawY.size - 1) {
        markers.put(
            my_canvas.pointsToDrawX[value].toInt(),
            my_canvas.pointsToDrawY[value].toInt(),
            1.0
       )
}
//Dictate the area of interest
//I used different values for each part of the car (for visibility)
for (value in 0..my_canvas.pointsToDrawYStepTwo.size - 1) {
        markers.put(
            my_canvas.pointsToDrawXStepTwo[value].toInt(),
            my_canvas.pointsToDrawYStepTwo[value].toInt(),
            255.0
        )
}

//Now we have set the markers, we use the watershed
//algorithm to generate a marked image
watershed(srcOriginal, markers)
//Plot this one. If it does what we want, proceed;
//otherwise edit your markers and repeat
val mPath1 = Environment.getExternalStorageDirectory().toString() + "/watershed.png"
    Imgcodecs.imwrite(mPath1,markers)
//Make the background black, and what we want to keep white
for (x in 0 until srcOriginal.rows()-1) {
        for (y in 0 until srcOriginal.cols()-1) {
            if(markers.get(x,y).get(0).equals(1.0)){
                markers.put(
                    x,
                    y,
                    0.0
                )
            }
            if((markers[x, y].get(0) == 255.0)){
                markers.put(
                    x,
                    y,
                    255.0
                )
            }
        }
    }

//Use a kernel to dilate the image, to not lose any detail on the outline
//I used a kernel of 3x3 pixels
val marker_tempo = Mat()
val dilatation = Mat()
markers.convertTo(marker_tempo, CvType.CV_8U)
val kernel = Mat(3, 3, CvType.CV_8U)
Imgproc.dilate(marker_tempo, dilatation, kernel)
//Plot again to check whether the dilation is according to our needs
//If not, repeat by using a smaller/bigger kernel, or more/less iterations
val mPath2 = Environment.getExternalStorageDirectory().toString() + "/dilatation.png"
Imgcodecs.imwrite(mPath2,dilatation)
//Now apply the mask we created on the initial image
val final = Mat()
Core.bitwise_and(srcOriginal, srcOriginal, final, dilatation)
//Plot the final result
val mPath = Environment.getExternalStorageDirectory().toString() + "/final.png"
Imgcodecs.imwrite(mPath,final)
Tecnologia da Net
  • 215
  • 3
  • 7
  • 23
Carlos Diego
  • 348
  • 5
  • 20
1

numpy.zeros_like basically creates an array the same shape as the input with all zeros in it.

https://numpy.org/doc/stable/reference/generated/numpy.zeros_like.html

In this case, you have a simple 2d array so you could roll your own just by indexing through the whole array (all dimensions) and setting everything to zero.

var marker = Array(srcOriginal.rows()) {Array(srcOriginal.cols()) {0} }
user2199860
  • 788
  • 4
  • 14
  • User I updated the code and now I am having an error when iterating over the matrix to fill with a new value. Return the error: java.lang.NumberFormatException: for the input string: "Mat [0 * 0 * CV_32FC1, isCont = true, isSubmat = false, nativeObj = 0x788fe35c80, dataAddr = 0x0]". U can help me? – Tecnologia da Net May 28 '20 at 14:53
  • It's hard to tell from that snippet, but it looks like you are trying to use something which is not a number where a number is expected. Can you post the code where you iterate over the matrix? – user2199860 May 28 '20 at 14:58
  • I asked a new question, if you can answer, here is this: https://stackoverflow.com/questions/62067408/for-input-string-mat-00cv-32fc1-iscont-true-issubmat-false-nativeobj-0x – Tecnologia da Net May 28 '20 at 15:03
  • now i got confused. Do I need to scroll based on the matrix size of 0 or based on the array of points I want to add? – Tecnologia da Net May 28 '20 at 15:40
  • The matrix and the image should be the same size. So walk the rows and columns and use: markers.put(row, col, data) – user2199860 May 28 '20 at 15:47
  • so, the mask is already the same size as the image .. my doubt is how to walk through the rows and columns to add the values. can you show me this in code? please – Tecnologia da Net May 28 '20 at 15:54
  • in this case would it be so? for (i in 0..my_canvas.pointsToDrawY.size) { markers.put(my_canvas.pointsToDrawY.get(i).toInt(), my_canvas.pointsToDrawY.get(i).toInt(), intArrayOf(0,0,255)) } So it assigns the value 255 of the matrix according to the positions of the pointDraw – Tecnologia da Net May 28 '20 at 15:59
  • 1
    for(x in 0..my_canvas.pointsToDrawX.size) { for(y in 0..my_canvas.pointsToDrawY.size) { markers.put(x,y, intArrayOf(0,0,255)) } } – user2199860 May 28 '20 at 16:01
  • yeah i understand – Tecnologia da Net May 28 '20 at 16:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/214836/discussion-between-tecnologia-da-net-and-user2199860). – Tecnologia da Net May 28 '20 at 16:04
  • user2199860 u can help me here? https://stackoverflow.com/questions/62159692/problem-in-image-segmentation-using-watershed-on-android – Tecnologia da Net Jun 02 '20 at 19:51