gocv
gocv copied to clipboard
Severe Memory Leak During Conversion between Image and gocv.Mat
Description
In this issue, it is reported that there is a severe memory leak during the conversion between image.Image and gocv.Mat types, specifically when these conversions are done within a goroutine pool. Despite the fact that all the images are reported as Empty, a significant amount
Steps to Reproduce
package main
import (
"fmt"
"image"
"time"
"github.com/panjf2000/ants"
"gocv.io/x/gocv"
)
// MatToMat converts a gocv.Mat to another gocv.Mat through an image representation
func MatToMat(mat gocv.Mat) gocv.Mat {
// Convert gocv.Mat to image.Image
img, _ := mat.ToImage()
// Convert image.Image back to gocv.Mat (RGB format)
reImg, _ := gocv.ImageToMatRGB(img)
// Ensure the new gocv.Mat is closed to prevent memory leaks
defer reImg.Close()
return reImg
}
// ImgToImg converts an image.Image to another image.Image through a gocv.Mat representation
func ImgToImg(img image.Image) image.Image {
// Convert image.Image to gocv.Mat (RGB format)
mat, _ := gocv.ImageToMatRGB(img)
// Convert gocv.Mat back to image.Image
reImg, _ := mat.ToImage()
return reImg
}
// imgInfo holds an image's gocv.Mat representation and its name
type imgInfo struct {
img gocv.Mat
name string
}
// sendGocv is a channel used to send imgInfo instances
var sendGocv = make(chan imgInfo)
// imgdict is a map that stores imgInfo instances
var imgdict = make(map[string]gocv.Mat)
// poolWait waits for all images to be processed and stored in imgdict
func poolWait(count int) {
for {
select {
case imginfo := <-sendGocv:
imgdict[imginfo.name] = imginfo.img
count--
// fmt.Println("Remaining", count, "Processed", len(imgdict))
if count == 0 {
return
}
}
}
}
// createAndRunPool creates a goroutine pool to run a specified task multiple times
func createAndRunPool(count, poolSize int, task func(text string)) {
p, err := ants.NewPool(poolSize)
if err != nil {
panic(err)
}
defer p.Release()
go func() {
for i := 0; i < count; i++ {
err = p.Submit(func() {
msg := fmt.Sprintf("%d", i)
task(msg)
})
if err != nil {
panic(err)
}
}
}()
poolWait(count)
}
func main() {
file := "./any_png.png"
runCount := 100
mat := gocv.IMRead(file, gocv.IMReadUnchanged)
fmt.Println("first")
createAndRunPool(runCount, 20, func(text string) {
newMat := MatToMat(mat)
sendGocv <- imgInfo{newMat, text}
})
fmt.Println("end")
time.Sleep(5 * time.Second)
fmt.Println("Second round")
createAndRunPool(runCount, 20, func(text string) {
newMat := MatToMat(mat)
sendGocv <- imgInfo{newMat, text}
})
fmt.Println("End")
time.Sleep(5 * time.Second)
for _, ig := range imgdict {
if ig.Empty() {
fmt.Println("Empty!")
}
}
}
Your Environment
- Operating System and version: Linux 5.4.0-126-generic #142-Ubuntu SMP Fri Aug 26 12:12:57 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
- OpenCV version used: 4.8.1
- How did you install OpenCV? make build
- GoCV version used: v0.35.0
- Go version: 1.22.1
- Did you run the
env.sh
orenv.cmd
script before trying togo run
orgo build
?
If my usage method is incorrect, please tell me the correct usage. Thank you
Hello @Sclock please see https://github.com/hybridgroup/gocv?tab=readme-ov-file#profiling for explanation.