some weird image error 我正在尝试将 gif 分割为 png 图像,但只有第一张图像很好,其余部分出现了一些颜色错误...
(我不使用任何库,只使用 java 1.8)
public class GifSplitter {
public static void main(String[] args) {
try {
splitGif(new File(FCFinder.getOS().getMc() + File.separator + "test.gif"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void splitGif(File file) throws IOException {
ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
reader.setInput(ImageIO.createImageInputStream(new FileInputStream(file)), false);
BufferedImage lastImage = reader.read(0);
ImageIO.write(lastImage, "PNG", new File(0 + ".png"));
for (int i = 1; i < reader.getNumImages(true); i++) {
BufferedImage readImage = reader.read(i);
BufferedImage image = new BufferedImage(readImage.getWidth(), readImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
ImageIO.write(image, "PNG", new File(i + ".png"));
}
}
}
答案 0 :(得分:3)
看起来您正在循环中创建一个空图像并将其写入文件。尝试对您的代码进行这个小改动:
public class GifSplitter {
public static void main(String[] args) {
try {
splitGif(new File(FCFinder.getOS().getMc() + File.separator + "test.gif"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void splitGif(File file) throws IOException {
ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
reader.setInput(ImageIO.createImageInputStream(new FileInputStream(file)), false);
BufferedImage lastImage = reader.read(0);
ImageIO.write(lastImage, "PNG", new File(0 + ".png"));
for (int i = 1; i < reader.getNumImages(true); i++) {
BufferedImage readImage = reader.read(i);
ImageIO.write(readImage, "PNG", new File(i + ".png"));
}
}
}
答案 1 :(得分:2)
这不是错误,很可能是启用了透明度的 GIF 图像,这是一项有助于减小整个图像文件大小的优化功能。
第一个 GIF 帧始终是完整图像,因此您必须对后续帧执行一些额外的后期处理才能获得实际输出。
所以基本上,当你用 reader.read(i)
读取每一帧时,它最初会是这样的:
处理后应该是这样的:
然后您可以将完全转换的帧转换为 PNG。
在此处(第 4 页)阅读有关 GIF 的“处置方法”的更多信息:https://cs.nyu.edu/courses/fall10/V22.0004-002/animatedGifs.pdf
示例代码(后处理部分):
public static final String DISPOSAL_PREVIOUS = "restoreToPrevious";
public static final String DISPOSAL_BACKGROUND = "restoreToBackgroundColor";
public static final String DISPOSAL_NONE = "none";
public static class ImageFrame {
private final int delay;
private final BufferedImage image;
private final String disposal;
private final int width, height;
public ImageFrame(BufferedImage image, int delay, String disposal) {
this.image = image;
this.delay = delay;
this.disposal = disposal;
this.width = -1;
this.height = -1;
}
public ImageFrame(BufferedImage image, int delay, String disposal, int width, int height) {
this.image = image;
this.delay = delay;
this.disposal = disposal;
this.width = width;
this.height = height;
}
public ImageFrame(BufferedImage image) {
this.image = image;
this.delay = -1;
this.disposal = null;
this.width = -1;
this.height = -1;
}
public BufferedImage getImage() {
return image;
}
public int getDelay() {
return delay;
}
public String getDisposal() {
return disposal;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
private static class ImageMetaData {
int index;
int delay;
String disposal;
int x;
int y;
private ImageMetaData(int index, int delay, String disposal, int x, int y) {
this.index = index;
this.delay = delay;
this.disposal = disposal;
this.x = x;
this.y = y;
}
}
private static ImageMetaData extractImageMetaData(ImageReader reader, int frameIndex) {
try {
var metadata = reader.getImageMetadata(frameIndex);
var root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_gif_image_1.0");
var gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0);
int delay = Integer.parseInt(gce.getAttribute("delayTime"));
String disposal = gce.getAttribute("disposalMethod");
int x = 0, y = 0;
var children = root.getChildNodes();
for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++) {
Node nodeItem = children.item(nodeIndex);
if ("ImageDescriptor".equalsIgnoreCase(nodeItem.getNodeName())) {
NamedNodeMap map = nodeItem.getAttributes();
x = Integer.parseInt(map.getNamedItem("imageLeftPosition").getNodeValue());
y = Integer.parseInt(map.getNamedItem("imageTopPosition").getNodeValue());
}
}
return new ImageMetaData(frameIndex, delay, disposal, x, y);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static Integer[] extractLogicalScreenSize(ImageReader reader) {
Integer[] size = null;
try {
var metadata = reader.getStreamMetadata();
if (metadata == null)
return null;
var globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName());
var globalScreenDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor");
if (globalScreenDescriptor != null && globalScreenDescriptor.getLength() > 0) {
IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreenDescriptor.item(0);
if (screenDescriptor != null) {
size = new Integer[2];
size[0] = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth"));
size[1] = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight"));
}
}
} catch (IOException e) {
e.printStackTrace();
}
return size;
}
private static List<ImageFrame> readGifFrames(File file) throws IOException {
var frames = new ArrayList<ImageFrame>(2);
try (var is = ImageIO.createImageInputStream(file)) {
var it = ImageIO.getImageReadersBySuffix("gif");
if (!it.hasNext())
throw new IOException("No supported reader for this format");
final var reader = it.next();
reader.setInput(is);
try {
final var size = extractLogicalScreenSize(reader);
int width = size != null ? size[0] : -1;
int height = size != null ? size[1] : -1;
BufferedImage master = null;
Graphics2D baseImage = null;
int numFrames = reader.getNumImages(true);
for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
BufferedImage img;
try {
img = reader.read(frameIndex);
} catch (IndexOutOfBoundsException e) {
break;
}
if (width == -1 || height == -1) {
width = img.getWidth();
height = img.getHeight();
}
ImageMetaData imageMetadata = extractImageMetaData(reader, frameIndex);
int x = imageMetadata.x;
int y = imageMetadata.y;
if (master == null) {
master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
baseImage = master.createGraphics();
baseImage.setBackground(new java.awt.Color(0, 0, 0, 0));
}
baseImage.drawImage(img, x, y, null);
//copy master image to image frame
var copy = new BufferedImage(master.getColorModel(), master.copyData(null), master.isAlphaPremultiplied(), null);
var imageFrame = new ImageFrame(copy, imageMetadata.delay, imageMetadata.disposal, width, height);
frames.add(imageFrame);
//Process disposal
if (DISPOSAL_PREVIOUS.equals(imageMetadata.disposal)) {
BufferedImage from = null;
//scan frames backwards (from current to first), search for last processed undisposed frame
for (int i = frameIndex - 1; i >= 0; i--) {
var frame = frames.get(i);
//scan previous undisposed frame
if (!DISPOSAL_PREVIOUS.equals(frame.getDisposal()) || frameIndex == 0) {
from = frame.getImage();
break;
}
}
if (from != null) {
master = new BufferedImage(from.getColorModel(), from.copyData(null), from.isAlphaPremultiplied(), null);
baseImage = master.createGraphics();
//clear everything with transparent pixels
baseImage.setBackground(new java.awt.Color(0, 0, 0, 0));
}
} else if (DISPOSAL_BACKGROUND.equals(imageMetadata.disposal)) {
baseImage.clearRect(x, y, img.getWidth(), img.getHeight());
}
} //end for
return frames;
} finally {
reader.dispose();
}
}
}
public static void main(String[] args) throws Exception {
File testGif = new File("/home/test/test.gif");
List<ImageFrame> frames = readGifFrames(testGif);
for (ImageFrame frame : frames) {
var frameImage = frame.getImage();
//convert frameImage to PNG
}
}
答案 2 :(得分:1)
您可以使用 glide 将 gif 分解为图像。试试这个:
final ArrayList<Bitmap> bitmaps = new ArrayList<>();
Glide.with(MainActivity.this)
.asGif()
.load("url")
.into(new SimpleTarget<GifDrawable>() {
@Override
public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition<? super GifDrawable> transition) {
try {
resource.start();
resource.setLoopCount(100);
Object GifState = resource.getConstantState();
assert GifState != null;
Field frameLoader = GifState.getClass().getDeclaredField("frameLoader");
frameLoader.setAccessible(true);
Object gifFrameLoader = frameLoader.get(GifState);
assert gifFrameLoader != null;
Field gifDecoder = gifFrameLoader.getClass().getDeclaredField("gifDecoder");
gifDecoder.setAccessible(true);
StandardGifDecoder standardGifDecoder = (StandardGifDecoder) gifDecoder.get(gifFrameLoader);
for (int i = 0; i < Objects.requireNonNull(standardGifDecoder).getFrameCount(); i++) {
standardGifDecoder.advance();
bitmaps.add(standardGifDecoder.getNextFrame());
Log.e("bitmapcheck", String.valueOf(bitmaps.size()));
}
} catch (Exception ex) {
Log.e("bitmapcheck", ex.getMessage());
}
}
});
依赖关系
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'