研究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的垃圾回收机制,可能会由于一些垃圾导致内存占用变大
可以通过设置垃圾回收解决
代码实现说明
- 通过调试ImageIO生成BufferedImage.TYPE_BYTE_GRAY的PNG图片的代码,研究生成PNG的过程
- 只保留必须的png文件结构:magic、IHDR、IDAT、IEND
- 具体实现,拷贝了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)); } }
来源:https://www.cnblogs.com/chencye/p/12204760.html