使用 OpenCV 和 Java 从 gif 帧中获取垫子
Getting Mats from frames in a gif using OpenCV and Java
我正在尝试使用 OpenCV 从 gif 中获取帧。我找到 Convert each animated GIF frame to a separate BufferedImage 并使用了第二个建议。我将其稍微修改为 return 一个垫子数组而不是 BufferedImages。
我尝试了两种方法从 gif 中获取 bufferedImages
。每个都提出了不同的问题。
根据上一贴的建议
BufferedImage fImage=ir.read(i);
程序调用了一个"ArrayIndexOutOfBoundsException: 4096"
使用上一个线程的原始代码。
BufferedImage fImage=ir.getRawImageType(i).createBufferedImage(ir.getWidth(i),ir.getHeight(i));
每一帧都是单调的颜色(虽然不是全黑),从 BufferedImage 派生的垫子是空的。
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
ArrayList<Mat> frames = new ArrayList<Mat>();
ImageReader ir = new GIFImageReader(new GIFImageReaderSpi());
ir.setInput(ImageIO.createImageInputStream(new File("ronPaulTestImage.gif")));
for(int i = 0; i < ir.getNumImages(true); i++){
BufferedImage fImage=ir.read(i);
//BufferedImage fImage=ir.getRawImageType(i).createBufferedImage(ir.getWidth(i), ir.getHeight(i));
fImage = toBufferedImageOfType(fImage, BufferedImage.TYPE_3BYTE_BGR);
//byte[] pixels = ((DataBufferByte) r.getRaster().getDataBuffer()).getData();
Mat m=new Mat();
//m.put(0,0,pixels);
m.put(0, 0,((DataBufferByte) fImage.getRaster().getDataBuffer()).getData());
if(i==40){
//a test, writes the mat and the image at the specified frame to files, exits
ImageIO.write(fImage,"jpg",new File("TestError.jpg"));
Imgcodecs.imwrite("TestErrorMat.jpg",m);
System.exit(0);
}
我没有使用 gif 库,也没有使用 Java 或 OpenCV,而是 ArrayIndexOutOfBoundsException: 4096
表示字典没有正确清除。你的 gif 有问题,我测试过它,它包含错误,某些帧没有足够清晰的代码。如果你的 GIF 解码器没有 check/handle 这种情况,那么它只会崩溃,因为它的字典增长超过 GIF 限制 4096/12bit
尝试另一个 GIF 而不是一些有问题的 GIF ...
已测试您的 gif,每帧大约有 7 个明码,总共包含 941 个错误(缺少明码导致字典溢出)
如果您有 GIF 解码器的源代码
然后只找到解码器的一部分,其中新项目被添加到字典并添加
if (dictionary_items<4096)
之前...如果您忽略错误的条目,图像看起来仍然正常,很可能是创建它的编码器编码不正确。
按照 Spektre 的建议,我找到了一个更好的 gif,它修复了单色缓冲图像。缺少可视垫是因为我在声明垫时使用了默认构造函数。
工作代码
public static ArrayList<Mat> getFrames(File gif) throws IOException{
ArrayList<Mat> frames = new ArrayList<Mat>();
ImageReader ir = new GIFImageReader(new GIFImageReaderSpi());
ir.setInput(ImageIO.createImageInputStream(gif));
for(int i = 0; i < ir.getNumImages(true); i++){
BufferedImage fImage=ir.read(i);
fImage = toBufferedImageOfType(fImage, BufferedImage.TYPE_3BYTE_BGR);
byte[] pixels = ((DataBufferByte) fImage.getRaster().getDataBuffer()).getData();
Mat m=new Mat(fImage.getHeight(), fImage.getWidth(), CvType.CV_8UC3);
m.put(0,0,pixels);
if(i==15){//a test, writes the mat and the image at the specified frame to files, exits
ImageIO.write(fImage,"jpg",new File("TestError.jpg"));
Imgcodecs.imwrite("TestErrorMat.jpg",m);
System.exit(0);
}
frames.add(m);
}
return frames;
}
和bytedeco opencv,获取gif第一帧的简单方法:
import java.awt.image.{BufferedImage, DataBufferByte}
import java.io.ByteArrayInputStream
import com.sun.imageio.plugins.gif.{GIFImageReader, GIFImageReaderSpi}
import javax.imageio.ImageIO
import javax.swing.JFrame
import org.bytedeco.javacv.{CanvasFrame, OpenCVFrameConverter}
import org.bytedeco.opencv.opencv_core.Mat
import org.opencv.core.CvType
def toMat(bi: BufferedImage): Mat = {
val convertedImage = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_3BYTE_BGR)
val graphics = convertedImage.getGraphics
graphics.drawImage(bi, 0, 0, null)
graphics.dispose()
val data = convertedImage.getRaster.getDataBuffer.asInstanceOf[DataBufferByte].getData
val mat = new Mat(convertedImage.getHeight, convertedImage.getWidth, CvType.CV_8UC3)
mat.data.put(data: _*)
mat
}
def show(image: Mat, title: String): Unit = {
val canvas = new CanvasFrame(title, 1)
canvas.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE)
canvas.showImage(new OpenCVFrameConverter.ToMat().convert(image))
}
val imageByteArrayOfGif = Array(....) // can be downloaded with java.net.HttpURLConnection
val ir = new GIFImageReader(new GIFImageReaderSpi())
val in = new ByteArrayInputStream(imageByteArray)
ir.setInput(ImageIO.createImageInputStream(in))
val bi = ir.read(0) // first frame of gif
val mat = toMat(bi)
show(mat, "buffered2mat")
我正在尝试使用 OpenCV 从 gif 中获取帧。我找到 Convert each animated GIF frame to a separate BufferedImage 并使用了第二个建议。我将其稍微修改为 return 一个垫子数组而不是 BufferedImages。
我尝试了两种方法从 gif 中获取 bufferedImages
。每个都提出了不同的问题。
根据上一贴的建议
BufferedImage fImage=ir.read(i);
程序调用了一个"ArrayIndexOutOfBoundsException: 4096"
使用上一个线程的原始代码。
BufferedImage fImage=ir.getRawImageType(i).createBufferedImage(ir.getWidth(i),ir.getHeight(i));
每一帧都是单调的颜色(虽然不是全黑),从 BufferedImage 派生的垫子是空的。
System.loadLibrary( Core.NATIVE_LIBRARY_NAME ); ArrayList<Mat> frames = new ArrayList<Mat>(); ImageReader ir = new GIFImageReader(new GIFImageReaderSpi()); ir.setInput(ImageIO.createImageInputStream(new File("ronPaulTestImage.gif"))); for(int i = 0; i < ir.getNumImages(true); i++){ BufferedImage fImage=ir.read(i); //BufferedImage fImage=ir.getRawImageType(i).createBufferedImage(ir.getWidth(i), ir.getHeight(i)); fImage = toBufferedImageOfType(fImage, BufferedImage.TYPE_3BYTE_BGR); //byte[] pixels = ((DataBufferByte) r.getRaster().getDataBuffer()).getData(); Mat m=new Mat(); //m.put(0,0,pixels); m.put(0, 0,((DataBufferByte) fImage.getRaster().getDataBuffer()).getData()); if(i==40){ //a test, writes the mat and the image at the specified frame to files, exits ImageIO.write(fImage,"jpg",new File("TestError.jpg")); Imgcodecs.imwrite("TestErrorMat.jpg",m); System.exit(0); }
我没有使用 gif 库,也没有使用 Java 或 OpenCV,而是 ArrayIndexOutOfBoundsException: 4096
表示字典没有正确清除。你的 gif 有问题,我测试过它,它包含错误,某些帧没有足够清晰的代码。如果你的 GIF 解码器没有 check/handle 这种情况,那么它只会崩溃,因为它的字典增长超过 GIF 限制 4096/12bit
尝试另一个 GIF 而不是一些有问题的 GIF ...
已测试您的 gif,每帧大约有 7 个明码,总共包含 941 个错误(缺少明码导致字典溢出)
如果您有 GIF 解码器的源代码
然后只找到解码器的一部分,其中新项目被添加到字典并添加
if (dictionary_items<4096)
之前...如果您忽略错误的条目,图像看起来仍然正常,很可能是创建它的编码器编码不正确。
按照 Spektre 的建议,我找到了一个更好的 gif,它修复了单色缓冲图像。缺少可视垫是因为我在声明垫时使用了默认构造函数。
工作代码
public static ArrayList<Mat> getFrames(File gif) throws IOException{
ArrayList<Mat> frames = new ArrayList<Mat>();
ImageReader ir = new GIFImageReader(new GIFImageReaderSpi());
ir.setInput(ImageIO.createImageInputStream(gif));
for(int i = 0; i < ir.getNumImages(true); i++){
BufferedImage fImage=ir.read(i);
fImage = toBufferedImageOfType(fImage, BufferedImage.TYPE_3BYTE_BGR);
byte[] pixels = ((DataBufferByte) fImage.getRaster().getDataBuffer()).getData();
Mat m=new Mat(fImage.getHeight(), fImage.getWidth(), CvType.CV_8UC3);
m.put(0,0,pixels);
if(i==15){//a test, writes the mat and the image at the specified frame to files, exits
ImageIO.write(fImage,"jpg",new File("TestError.jpg"));
Imgcodecs.imwrite("TestErrorMat.jpg",m);
System.exit(0);
}
frames.add(m);
}
return frames;
}
和bytedeco opencv,获取gif第一帧的简单方法:
import java.awt.image.{BufferedImage, DataBufferByte}
import java.io.ByteArrayInputStream
import com.sun.imageio.plugins.gif.{GIFImageReader, GIFImageReaderSpi}
import javax.imageio.ImageIO
import javax.swing.JFrame
import org.bytedeco.javacv.{CanvasFrame, OpenCVFrameConverter}
import org.bytedeco.opencv.opencv_core.Mat
import org.opencv.core.CvType
def toMat(bi: BufferedImage): Mat = {
val convertedImage = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_3BYTE_BGR)
val graphics = convertedImage.getGraphics
graphics.drawImage(bi, 0, 0, null)
graphics.dispose()
val data = convertedImage.getRaster.getDataBuffer.asInstanceOf[DataBufferByte].getData
val mat = new Mat(convertedImage.getHeight, convertedImage.getWidth, CvType.CV_8UC3)
mat.data.put(data: _*)
mat
}
def show(image: Mat, title: String): Unit = {
val canvas = new CanvasFrame(title, 1)
canvas.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE)
canvas.showImage(new OpenCVFrameConverter.ToMat().convert(image))
}
val imageByteArrayOfGif = Array(....) // can be downloaded with java.net.HttpURLConnection
val ir = new GIFImageReader(new GIFImageReaderSpi())
val in = new ByteArrayInputStream(imageByteArray)
ir.setInput(ImageIO.createImageInputStream(in))
val bi = ir.read(0) // first frame of gif
val mat = toMat(bi)
show(mat, "buffered2mat")