image - 重复调用 image.png.Decode() 导致内存不足错误

标签 image go

我正在尝试做我原先认为非常简单的事情。即:

对于输入文件列表中的每个文件:

  1. 用png.Decode()打开文件
  2. 扫描文件中的每个像素并测试它是否为“灰色”。
  3. 返回图像中“灰色”像素的百分比。

这是我调用的函数:

func greyLevel(fname string) (float64, string) {
    f, err := os.Open(fname)
    if err != nil {
            return -1.0, "can't open file"
    }
    defer f.Close()

    i, err := png.Decode(f)
    if err != nil {
            return -1.0, "unable to decode"
    }

    bounds := i.Bounds()

    var lo uint32 = 122 // Low grey RGB value.
    var hi uint32 = 134 // High grey RGB value.
    var gpix float64    // Grey pixel count.
    var opix float64    // Other (non-grey) pixel count.
    var tpix float64    // Total pixels.

    for x := bounds.Min.X; x < bounds.Max.X; x++ {
            for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
                    r, g, b, _ := i.At(x, y).RGBA()
                    if ((r/255)-1 > lo && (r/255)-1 < hi) &&
                            ((g/255)-1 > lo && (g/255)-1 < hi) &&
                            ((b/255)-1 > lo && (b/255)-1 < hi) {
                            gpix++
                    } else {
                            opix++
                    }
                    tpix++
            }
    }
    return (gpix / tpix) * 100, ""
} 

func main() {
    srcDir := flag.String("s", "", "Directory containing image files.")
    threshold := flag.Float64("t", 65.0, "Threshold (in percent) of grey pixels.")
    flag.Parse()

    dirlist, direrr := ioutil.ReadDir(*srcDir)
    if direrr != nil {
            log.Fatalf("Error reading %s: %s\n", *srcDir, direrr)
    }

    for f := range dirlist {
            src := path.Join(*srcDir, dirlist[f].Name())

            level, msg := greyLevel(src)

            if msg != "" {
                    log.Printf("error processing %s: %s\n", src, msg)
                    continue
            }

            if level >= *threshold {
                    log.Printf("%s is grey (%2.2f%%)\n", src, level)
            } else {
                    log.Printf("%s is not grey (%2.2f%%)\n", src, level)
            }
    }
}

文件相对较小(960x720,8 位 RGB)

我正在调用 ioutil.ReadDir() 生成文件列表,遍历 slice 并调用 greyLevel()。

在大约 155 个文件(超过 4000 个文件的列表中)之后,脚本出现 panic :

runtime: memory allocated by OS not in usable range
runtime: out of memory: cannot allocate 2818048-byte block (534708224 in use)
throw: out of memory

我认为我缺少一些简单的东西。我以为 Go 会取消分配在 greyLevels() 中分配的内存,但我想不会吧?

跟进:

在每次调用 greyLevels 后插入 runtime.GC() 后,内存使用量就会平衡。昨晚我正在测试大约 800 张图像,然后停了下来。今天我让它运行整个输入集,大约 6800 张图像。

1500 张图片后,top 看起来像这样:

top - 10:30:11 up 41 days, 11:47,  2 users,  load average: 1.46, 1.25, 0.88
Tasks: 135 total,   2 running, 131 sleeping,   1 stopped,   1 zombie
Cpu(s): 49.8%us,  5.1%sy,  0.2%ni, 29.6%id, 15.0%wa,  0.0%hi,  0.3%si,  0.0%st
Mem:   3090304k total,  2921108k used,   169196k free,     2840k buffers
Swap:  3135484k total,    31500k used,  3103984k free,   640676k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
28474 mtw       20   0 2311m 1.8g  412 R   99 60.5  16:48.52 8.out

并在处理另外 5000 张图像后保持稳定。

最佳答案

看来您使用的是 32 位机器。程序很可能会耗尽内存,因为 Go 的垃圾收集器是保守的。保守的垃圾收集器可能无法检测到某些内存区域不再使用。目前在 Go 程序中除了避免垃圾收集器无法处理的数据结构(例如:struct {...; binaryData [256]byte})之外没有解决方法

尝试在调用函数 greyLevel 的循环的每次迭代中调用 runtime.GC()。也许它会帮助程序处理更多图像。

如果调用 runtime.GC() 无法改善这种情况,您可能需要更改策略,以便程序每次运行处理的 PNG 文件数量较少。

关于image - 重复调用 image.png.Decode() 导致内存不足错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9868214/

相关文章:

go - 关闭响应流

go - 为什么运行 `go get golang.org/x/tools/cmd/godoc` 时没有创建二进制文件?

javascript - 我的 JavaScript 图片库 slider 的右侧 slider 无法正常工作,而左侧的 slider 可以正常工作

java - 有没有办法使用 Java 截取屏幕截图并将其保存到某种图像中?

image - Typo3 如何处理我上传的图片?

string - 戈朗 : read text file line by line of int strings

go - Go 中的接口(interface)层次结构

golang 为 slice slice (二维 slice )中的现有元素赋值

Java调整图像的中心

php - 在php中将PDF转换为图像