无法使用Golang的ImageMagick库转换大图像。

huangapple go评论72阅读模式
英文:

Cannot convert big image with Golangs ImageMagick library

问题

我正在尝试使用Golang的ImageMagick库加载一个大图像(780MB),并将图像的格式从PNG转换为PTIF(金字塔式TIFF)。当我运行程序时,可能会发生不同的情况:

  • 有时会关闭Vscode(内存消耗高)
  • 只是挂起。当它挂起时,它不会停止程序,但也不再使用任何资源(观察htop)。
  • 有时会出现错误runtime error: gobytes: length out of range,这似乎发生在超过cgo中的maxAlloc值时(ImageMagick使用C代码)。错误来自https://go.dev/src/runtime/string.go的304行。
  • 过一段时间后,调试会停止,并在Vscode中显示消息“进程x以状态-9退出”。

当我设置断点时,一切都很顺利,直到byteBuf := mim.mw.GetImageBlob()在StoreImage函数中。

我在一台联想Linux笔记本上有16GB的RAM,并且已经将交换文件增加到了8GB,但这只对使用ImageMagick的CLI更改格式有所帮助(convert collage.png test.ptif),但在应用程序中仍然无法正常工作。我猜这是一个内存问题,但是16GB的内存不应该足够处理780MB的图像吗?有什么建议吗?该代码可以处理较小的图像。

package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"

	"gopkg.in/gographics/imagick.v3/imagick"
)

type MyImageMagick struct {
	mw *imagick.MagickWand
}

func (mim *MyImageMagick) convert() {
	f, err := os.Open("collage.png")
	if err != nil {
		log.Fatal(err)
	}
	if err := mim.LoadImage(f); err != nil {
		log.Fatal(err)
	}
	r, err := mim.StoreImage("ptif")
	if err != nil {
		log.Fatal(err)
	}
	t, err := os.Create("testptif")
	if err != nil {
		log.Fatal(err)
	}
	n, err := io.Copy(t, r)
	if err != nil {
		log.Fatal(err)
	}
}
func (mim *MyImageMagick) LoadImage(reader io.Reader) error {
	var buf bytes.Buffer
	if _, err := buf.ReadFrom(reader); err != nil {
		return err
	}
	if err := mim.mw.ReadImageBlob(buf.Bytes()); err != nil {
		return err
	}
	return nil
}
func (mim *MyImageMagick) StoreImage(format string) (io.Reader, error) {
	if err := mim.mw.SetImageFormat(format); err != nil {
		return nil, err
	}
	byteBuf := mim.mw.GetImageBlob()
	return bytes.NewReader(byteBuf), nil
}
func main() {
	imagick.Initialize()
	defer imagick.Terminate()
	mim := &MyImageMagick{mw: imagick.NewMagickWand()}
	mim.convert()
}
英文:

I'm trying to load a big image (780MB) with Golangs ImageMagick library and change the format of the image from PNG to PTIF (pyramidal tiff). When I run the program different things can happen:

  • it will sometimes close Vscode (high memory consuption)
  • just hang. When it hangs it doesn't stop the program but it also doesn't use any resources anymore (watching htop).
  • Sometimes I get the error runtime error: gobytes: length out of range this seems to happen when it gets bigger than the value in maxAlloc in cgo (ImageMagick is using C code). The error comes from https://go.dev/src/runtime/string.go line 304.
  • It will just stop debugging after a while with the message in Vscode "Process x has exited with status -9"

When I set breakpoints it goes well until byteBuf := mim.mw.GetImageBlob() in the StoreImage function.

I have 16GB RAM on a Lenovo Linux Laptop and I already increased my swapfile to 8GB, but this only helped me with changing the format in the CLI with ImageMagick (convert collage.png test.ptif), it still doesn't work in the app. I'm guessing it is a memory issue but shouldn't 16GB be enough for a 780MB image? Any suggestions? The code works with smaller images.

package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"gopkg.in/gographics/imagick.v3/imagick"
)
type MyImageMagick struct {
mw *imagick.MagickWand
}
func (mim *MyImageMagick) convert() {
f, err := os.Open("collage.png")
if err != nil {
log.Fatal(err)
}
if err := mim.LoadImage(f); err != nil {
log.Fatal(err)
}
r, err := mim.StoreImage("ptif")
if err != nil {
log.Fatal(err)
}
t, err := os.Create("testptif")
if err != nil {
log.Fatal(err)
}
n, err := io.Copy(t, r)
if err != nil {
log.Fatal(err)
}
}
func (mim *MyImageMagick) LoadImage(reader io.Reader) error {
var buf bytes.Buffer
if _, err := buf.ReadFrom(reader); err != nil {
return err
}
if err := mim.mw.ReadImageBlob(buf.Bytes()); err != nil {
return err
}
return nil
}
func (mim *MyImageMagick) StoreImage(format string) (io.Reader, error) {
if err := mim.mw.SetImageFormat(format); err != nil {
return nil, err
}
byteBuf := mim.mw.GetImageBlob()
return bytes.NewReader(byteBuf), nil
}
func main() {
imagick.Initialize()
defer imagick.Terminate()
mim := &MyImageMagick{mw: imagick.NewMagickWand()}
mim.convert()
}

答案1

得分: 2

libvips可以快速进行此类处理,并且使用相对较少的内存。例如:

$ ls -l x.png
-rw-r--r-- 1 john john 995241537 Mar  8 09:01 x.png
$ vipsheader x.png
x.png: 15000x26319 uchar, 3 bands, srgb, pngload
$ /usr/bin/time -f %M:%e vips copy x.png x.tif[pyramid,tile,compression=jpeg]
213820:12.07

因此,一个1GB的PNG文件(15,000 x 26,000像素)在12秒内转换为金字塔式的TIFF,并且需要210MB的内存。

有几个Go绑定,但不幸的是,它们都似乎存在各种问题。也许你可以直接使用vips命令?转换为金字塔式TIFF的示例命令如下:

vips copy input-file output-file.tiff[pyramid,tile,compression=jpeg]

你可以设置其他各种选项来进行TIFF写入:

https://www.libvips.org/API/current/VipsForeignSave.html#vips-tiffsave

英文:

libvips can do this kind of processing quickly and using relatively little memory. For example:

$ ls -l x.png
-rw-r--r-- 1 john john 995241537 Mar  8 09:01 x.png
$ vipsheader x.png
x.png: 15000x26319 uchar, 3 bands, srgb, pngload
$ /usr/bin/time -f %M:%e vips copy x.png x.tif[pyramid,tile,compression=jpeg]
213820:12.07

So a 1gb PNG file (15,000 x 26,000 pixels) converted to a pyramidal TIFF in 12 seconds and needed 210mb of memory.

There are several Go bindings, but all of them seem to have various issues, unfortunately. Perhaps you could just shell out to the vips command? Conversion to pyramidal tiff is eg.:

vips copy input-file output-file.tiff[pyramid,tile,compression=jpeg]

You can set various other options for the tiff write:

https://www.libvips.org/API/current/VipsForeignSave.html#vips-tiffsave

huangapple
  • 本文由 发表于 2023年3月7日 22:37:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75663363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定