pgm转png

研究pgm转png比较消耗内存的问题

  • 2020.01.14 通过Java的ImageIO生成png与jpg图片,5000*5000的pgm需要消耗300M内存以上
  • 2020.01.15 通过jconsole, mat等工具,定位问题是由于在内存中生成整个png所有需要的数据,导致的问题
  • 2020.01.16 研究方向为根据png的实际文件结构,自己写代码生成png图片,基于昨天的研究:png支持灰度图

使用jdk12测试:

  • 将5000*5000的pgm转换为png消耗12M左右
  • 将10000*10000的pgm转换为png消耗14M左右

由于需要运行java类库,实际使用估计在2M左右
如果连续转换,由于java的垃圾回收机制,可能会由于一些垃圾导致内存占用变大
可以通过设置垃圾回收解决

代码实现说明

  1. 通过调试ImageIO生成BufferedImage.TYPE_BYTE_GRAY的PNG图片的代码,研究生成PNG的过程
  2. 只保留必须的png文件结构:magic、IHDR、IDAT、IEND
  3. 具体实现,拷贝了com.sun.imageio.plugins.png的实现

执行pgm转png

java -jar pgm-to-png.jar pgm=5000-5000.pgm

参数说明:

  • pgm: 源pgm文件地址,若只有文件名,则为运行时目录
  • png: 转换后的png文件地址,若为空,则将pgm文件路径换成png后缀为png文件地址
  • start: 启动倒数秒数
  • end: 结束倒数秒数

使用jconsole监控内存使用情况

设置为1秒采样一次:命令行执行jconsole -interval=1

  • 使用远程连接,方便重试
    java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=7077 -Dcom.sun.management.jmxremote.local.only=true -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1 -jar pgm-to-png.jar pgm=5000-5000.pgm start=5
  • 本地在jconsole中,直接选择进行ID也可以,多次执行不太方便

创建pgm文件

java -cp pgm-to-png.jar com.bdr.demo.PgmCreator无参数默认创建5000*5000的文件

参数说明:

  • width: 灰度图长度
  • height: 灰度图高度
  • file: 灰度图保存地址

示例:
java -cp pgm-to-png.jar com.bdr.demo.PgmCreator width=100 height=100 file=test.pgm

代码

PgmToPng

import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class PgmToPng {

    public static void main(String[] args) throws Exception {

        Map<String, String> argMap = new HashMap<>();
        for (String arg : args) {
            String[] argArr = arg.split("=");
            argMap.put(argArr[0], argArr[1]);
        }

        String source = argMap.get("pgm");
        if (!Files.isRegularFile(Paths.get(source))) {
            System.out.println("source file not exist: " + source);
            return;
        }

        String dest = argMap.get("png");
        dest = dest == null ? source.substring(0, source.lastIndexOf('.')) + ".png" : dest;

        if (Files.isRegularFile(Paths.get(dest))) {
            System.out.println("delete file: " + dest);
            Files.deleteIfExists(Paths.get(dest));
        }

        System.out.println("source file: " + source);
        System.out.println("dest file: " + dest);

        System.out.println("start...");
        countDown(argMap.get("start"));
        new PgmToPng().convertPgmToPng(source, dest);
        System.out.println("convert over: " + dest);
        System.out.println("end.");
        countDown(argMap.get("end"));
    }

    private static void countDown(String secondStr) throws Exception {
        if (secondStr == null) {
            return;
        }
        try {
            countDown(Integer.parseInt(secondStr));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void countDown(int second) throws Exception {
        for (int t = second; t > 0; t--) {
            TimeUnit.SECONDS.sleep(1);
            System.out.println(t + "s...");
        }
    }

    private void convertPgmToPng(String source, String dest) throws Exception {
        final BufferedInputStream in = new BufferedInputStream(new FileInputStream(source));
        ImageOutputStream out = new FileImageOutputStream(new File(dest));
        try (in; out) {
            if (!"P5".equals(next(in))) {
                throw new IOException("File  is not a binary PGM image.");
            }
            final int col = Integer.parseInt(next(in));
            final int row = Integer.parseInt(next(in));
            final int max = Integer.parseInt(next(in));
            if (max < 0 || max > 255) {
                throw new IOException("The image's maximum gray value must be in range [0, " + 255 + "].");
            }
            writeMagic(out);
            writeIHDR(out, col, row);
            IDATOutputStream os = new IDATOutputStream(out, 32768, 4);
            byte[] curRow = new byte[col];
            for (int i = 0; i < row; ++i) {
                for (int j = 0; j < col; ++j) {
                    final int p = in.read();
                    if (p < 0 || p > max) {
                        throw new IOException("Pixel value " + p + " outside of range [0, " + max + "].");
                    }
                    curRow[j] = (byte) p;
                }
                writeIDAT(os, curRow, col);
                os.flush();
            }
            os.finish();
            writeIEND(out);
            out.flush();
        }
    }

    protected void writeMagic(ImageOutputStream stream) throws Exception {
        byte[] magic = {(byte) 137, 80, 78, 71, 13, 10, 26, 10};
        stream.write(magic);
    }

    protected void writeIHDR(ImageOutputStream stream, int col, int row) throws Exception {
        ChunkStream cs = new ChunkStream(0x49484452, stream); // PNGImageReader.PNGImageReader.IHDR_TYPE
        cs.writeInt(col); // IHDR_width
        cs.writeInt(row); // IHDR_height
        cs.writeByte(8); // IHDR_bitDepth
        cs.writeByte(0); // IHDR_colorType
        cs.writeByte(0); // IHDR_compressionMethod
        cs.writeByte(0); // IHDR_filterMethod
        cs.writeByte(0); // IHDR_interlaceMethod
        cs.finish();
    }

    protected void writeIDAT(ImageOutputStream os, byte[] currRow, int col) throws Exception {
        os.write(0);
        os.write(currRow, 0, col);
    }

    protected void writeIEND(ImageOutputStream stream) throws Exception {
        ChunkStream cs = new ChunkStream(0x49454e44, stream);
        cs.finish();
    }

    private static String next(final InputStream stream) throws IOException {
        final List<Byte> bytes = new ArrayList<>();
        while (true) {
            final int b = stream.read();
            if (b != -1) {
                final char c = (char) b;
                if (c == '#') {
                    int d;
                    do {
                        d = stream.read();
                    } while (d != -1 && d != '\n' && d != '\r');
                } else if (!Character.isWhitespace(c)) {
                    bytes.add((byte) b);
                } else if (!bytes.isEmpty()) {
                    break;
                }
            } else {
                break;
            }
        }
        final byte[] bytesArray = new byte[bytes.size()];
        for (int i = 0; i < bytesArray.length; ++i)
            bytesArray[i] = bytes.get(i);
        return new String(bytesArray);
    }
}

PgmCreator

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class PgmCreator {
    public static void main(String[] args) throws Exception {
        Map<String, String> argMap = new HashMap<>();
        for (String arg : args) {
            String[] argArr = arg.split("=");
            argMap.put(argArr[0], argArr[1]);
        }
        String widthStr = argMap.get("width");
        int width = widthStr == null ? 5000 : Integer.parseInt(widthStr);

        String heightStr = argMap.get("height");
        int height = heightStr == null ? 5000 : Integer.parseInt(heightStr);

        String file = argMap.get("file");
        file = file == null ? width + "-" + height + ".pgm" : file;

        PGMIO.write(PGMUtils.createArc(width, height), new File(file));
    }
}

posted @ 2020-01-17 10:58  chencye  阅读(1584)  评论(0编辑  收藏  举报