gocv icon indicating copy to clipboard operation
gocv copied to clipboard

Most Efficient Way to Scan/Access/Set gocv.Mat Values

Open frytoli opened this issue 1 year ago • 0 comments

This is a bit of a two-fold question.

Firstly, I am curious about the most performant way to access and set values in a gocv.Mat.

Related to this first question, OpenCV has a report about the most performant way to scan images with their C++ code here: https://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html. In this report, they show that the most efficient ways to scan an image are by iterating over a pointer to each row in the image and using the built-in LUT() function. Go doesn't allow indexing pointers to slices, and I attempted to use gocv.LUT() in my code, and the function returns and entirely blank (black) image. So second question: I'm curious if anyone can reproduce this issue.

Thank you in advance for your help and input!

More details in the description below:

Description

Issue 1: I'm aware of and have been using gocv's .SetUCharAt(), and it also looks like some previous discussions concluded with this as the best result. I've also seen some discussion on other sites about converting gocv.Mat to []bytes, accessing and manipulating the values in the byte array, then converting such back to gocv.Mat. And lastly the official OpenCV docs (above) mention a lookup table being the most performant. Any comments on this? Is there a way to access an image a row at a time?

Issue 2: I have a 2D grayscale image, and I'm trying to remove all pixels whose occurrence count in the image is less than a given threshold. My algorithm works by 1. iterating over the image and saving the count of each pixel value in an array, 2. building the lookup table as a gocv.Mat by iterating over the results of step 1's array and setting pixel values accordingly, and 3. replacing the values with gocv.LUT(). I'll include my code below. The result of gocv.LUT() is a 2D gocv.Mat of 0s, and it should not be.

Steps to Reproduce LUT Issue

Where img is a 2D grayscale image, threshold is an arbitrary int, fill is a uint8 value that will replace all values that have a count less than threshold. In my test cases I have used 0.

// Removel al pixel values that occur less than a give threshold
// References from:
// * https://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html
func removeSmallObjects(img gocv.Mat, threshold int, fill uint8) gocv.Mat {
	// Keep track of value counts
	objects := [256]int{} //  Default values are 0
	// Iterate over image and count value occurrence
	for r := 0; r < img.Rows(); r++ {
		for c := 0; c < img.Cols(); c++ {
		    v := img.GetUCharAt(r, c)
		    objects[int(v)] += 1
		}
	}

	// Create a lookup table
	table := gocv.NewMatWithSize(1, 256, gocv.MatTypeCV8U)
	defer table.Close()
	for i, v := range objects {
		if v < threshold {
		    table.SetUCharAt(1, i, fill) // Fill with provided value
		} else {
		    table.SetUCharAt(1, i, uint8(i)) // Otherwise, keep the value as itself
		}
	}
	// Remove pixels that have an occurrence count less than the provided threshold
	gocv.LUT(img, table, &img)

	// Return
	return img
}

This is my code that calls this function:

// Convert image to gocv.Mat
imgMat, _ := gocv.ImageGrayToMatGray(gray)
defer imgMat.Close()

// Binary Inverse threshold (black background)
gocv.Threshold(imgMat, &imgMat, 127, 255, gocv.ThresholdBinaryInv)

// Remove small objects
imgClean = removeSmallObjects(imgMat, 64, uint8(0))

// imgClean ends up being the same dimension of imgMat, but made up entirely of 0s

Your Environment

  • Operating System and version: Ubuntu 22.04 (Docker)
  • OpenCV version used: 4.7.0
  • How did you install OpenCV?
RUN git clone --depth=1 --branch v0.33.0 https://github.com/hybridgroup/gocv.git && \
  cd gocv && make install
  • GoCV version used: 0.33.0
  • Go version: 1.20
  • Did you run the env.sh or env.cmd script before trying to go run or go build? No

frytoli avatar Jun 16 '23 22:06 frytoli