java JPG等图片格式转成PGM

3 篇文章 0 订阅
1 篇文章 0 订阅

不想看废话的直接跳到实现部分

 

引言

原本以为网上会有很多这方面的工具类可以实现,但是发现寥寥无几,而且大部分还都是其他的语言的。

最近在研究人脸的识别技术,大部分的人脸识别开源库都是使用pgm的图片格式作为识别图片格式。

因此测试和使用时,必然要把jpg等彩色图片转为pgm的灰度图片格式使用。

 

在讲方法之前,先进行一下图片的格式科普:

PPM(Portable PixMap)是portable像素图片,是由netpbm项目定义的一系列的portable图片格式中的一个。这些图片格式都相对比较容易处理,跟平台无关,所以称之为portable,就是比较直接的图片格式。

比如PPM,其实就是把每一个点的RGB分别保存起来。

所以,PPM格式的文件是没有压缩的,相对比较大,但是由于图片格式简单,一般作为图片处理的中间文件(不会丢失文件信息),或者作为简单的图片格式保存。

netpbm的几种图片格式是通过其表示的颜色类型来区别的,PBM是单色图,只有黑色和白色,PGM是灰度图片,PPM是代表完整的RGB颜色的图片,有时也统称为PNM格式。

 

科普1

那么接下来单纯讲 PGM的文件存储信息格式:

PGM

该格式文件存储灰度图形,也就是这里每个像素使用一个值来表示而不是3个(R,G,B)。

同PPM唯一不同的是头部用P2和P5,分别表示用ASCII和字节码来表示数据。

例如 :

这是一张PGM格式图片的形式 

第一行 P5 

720 1280 是图片尺寸

255 代表文件数据部分可能出现的像素灰度值的最大值,后面的便是图片的内容数据 。

 

科普2

灰度图是如何计算的生成的:

灰度图直观地讲就是将原来的RGB图像转换为只有灰度级的图像,做这一步处理也比较简单,只要把每个像素点的RGB值拿出来,算一下他们的平均值(R+G+B)/3,然后再替换原来的RGB值就OK了。

 

科普3

在使用Java SE中类来读取图片时,通常使用ImageIO包中的类来读取图片,但是有个缺点就是默认支持的图片格式很少,Java 8中javax.imageio支持的图片格式有:

从图中看出库仅仅支持JPEG、PNG、BMP、WBMP以及GIF这些图片格式。如果想读取其他格式时应该怎么办?网上搜索到了一个Java ImageIO的扩展插件——TwelveMonkeys,安装后ImageIO就可以支持多种图片格式,这个有一个好处就是原先读取图片的代码不需要改变,读取图片还是使用 javax.imageio 包。

ps: imageio-pnm里面有一个大坑 那就是在转成pgm的时候 它生成的文件头 是P6

但是在这里我直接用原生的字节流读取写入,因此不用理会 TwelveMonkeys

只是在读取jpg png 图片格式的时候需要用到 imageIo

 

实现

在项目: https://gist.github.com/armanbilge/3276d80030d1caa2ed7c#file-pgmio-java-L2  的基础上进行了完善

完善的项目演示代码:

https://gitee.com/Keith404/PGMIO

package utlis;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * A utility class for reading and writing PGM images. Methods use integers to represent unsigned bytes.
 * <p>
 * Does not fully conform to the PGM specification because currently there is no support for:
 * <ul>
 * <li>More than one image per file</li>
 * <li>Images with more than 256 shades of gray</li>
 * <li>Comments within the raster</li>
 * </ul>
 * github https://gist.github.com/armanbilge/3276d80030d1caa2ed7c#file-pgmio-java-L2
 *
 * @author Arman Bilge
 */
public final class PgmIo {

    private static final String MAGIC = "P5";
    /**
     * Character indicating a comment.
     */
    private static final char COMMENT = '#';
    /**
     * The maximum gray value.
     */
    private static final int MAXVAL = 255;

    /**
     * Reads a grayscale image from a file in PGM format.
     *
     * @param file the PGM file read from
     * @return two-dimensional byte array representation of the image
     * @throws IOException
     */
    public static int[][] read(final File file) throws IOException {
        final BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file));
        try {
            if (!next(stream).equals(MAGIC))
                throw new IOException("File " + file + " is not a binary PGM image.");
            final int col = Integer.parseInt(next(stream));
            final int row = Integer.parseInt(next(stream));
            final int max = Integer.parseInt(next(stream));
            if (max < 0 || max > MAXVAL)
                throw new IOException("The image's maximum gray value must be in range [0, " + MAXVAL + "].");
            final int[][] image = new int[row][col];
            for (int i = 0; i < row; ++i) {
                for (int j = 0; j < col; ++j) {
                    final int p = stream.read();
                    if (p == -1)
                        throw new IOException("Reached end-of-file prematurely.");
                    else if (p < 0 || p > max)
                        throw new IOException("Pixel value " + p + " outside of range [0, " + max + "].");
                    image[i][j] = p;
                }
            }
            return image;
        } finally {
            stream.close();
        }
    }

    /**
     * Finds the next whitespace-delimited string in a stream, ignoring any comments.
     *
     * @param stream the stream read from
     * @return the next whitespace-delimited string
     * @throws IOException
     */
    private static String next(final InputStream stream) throws IOException {
        final List<Byte> bytes = new ArrayList<Byte>();
        while (true) {
            final int b = stream.read();

            if (b != -1) {

                final char c = (char) b;
                if (c == COMMENT) {
                    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.size() > 0) {
                    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);
    }

    /**
     * Writes a grayscale image to a file in PGM format.
     *
     * @param image a two-dimensional byte array representation of the image
     * @param file  the file to write to
     * @throws IllegalArgumentException
     * @throws IOException
     */
    public static void write(final int[][] image, final File file) throws IOException {
        write(image, file, MAXVAL);
    }


    public static void write(BufferedImage image, final File file) throws IOException {
        int[][] rgbArray = new int[image.getHeight()][image.getWidth()];
        for (int j = 0; j < image.getHeight(); j++) {
            for (int i = 0; i < image.getWidth(); i++) {
                int p = image.getRGB(i, j);
                int a = (p >> 24) & 0xff;
                int r = (p >> 16) & 0xff;
                int g = (p >> 8) & 0xff;
                int b = p & 0xff;
                int avg = (r + g + b) / 3;
                p = (a << 24) | (avg << 16) | (avg << 8) | avg;
                //newImg.setRGB(i, j, p);
                //image.setRGB(i, j, p);
                rgbArray[j][i] = avg;
            }
        }
        write(rgbArray, file);
    }

    public static void write(File imageFile, final File file) throws IOException {
        try {
            BufferedImage bf = ImageIO.read(imageFile);
            write(bf, file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Writes a grayscale image to a file in PGM format.
     *
     * @param image  a two-dimensional byte array representation of the image
     * @param file   the file to write to
     * @param maxval the maximum gray value
     * @throws IllegalArgumentException
     * @throws IOException
     */
    public static void write(final int[][] image, final File file, final int maxval) throws IOException {
        if (maxval > MAXVAL)
            throw new IllegalArgumentException("The maximum gray value cannot exceed " + MAXVAL + ".");
        final BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
        try {
            stream.write(MAGIC.getBytes());
            stream.write("\n".getBytes());
            stream.write(Integer.toString(image[0].length).getBytes());
            stream.write(" ".getBytes());
            stream.write(Integer.toString(image.length).getBytes());
            stream.write("\n".getBytes());
            stream.write(Integer.toString(maxval).getBytes());
            stream.write("\n".getBytes());

            for (int i = 0; i < image.length; i++) {
                for (int j = 0; j < image[0].length; j++) {
                    final int p = image[i][j];
                    if (p < 0 || p > maxval)
                        throw new IOException("Pixel value " + p + " outside of range [0, " + maxval + "].");
                    stream.write(image[i][j]);
                }
            }

            //you can delete println
            System.out.println("PGM create successful, file name :" + file.getName() + "\nwidth:" + image[0].length + ",height:" + image.length);

        } finally {
            stream.close();
        }
    }

}

 

测试使用:

实现了重载write方法,可以传入文件,也可以传入一个图片流。

第一个参数为 需要转换的源图片文件,第二个参数为 输出的文件。

import org.junit.Test;
import utlis.PgmIo;


import java.io.File;
import java.io.IOException;


/**
* @author: create by Keith
* @version: v1.0
* @description: PACKAGE_NAME
* @date:2020/5/28
**/
public class PgmIoTest {


    @Test
    public void pgmWriteTest() {
        try {
            PgmIo.write(new File("image/sa1.jpg"), new File("image/sa1.pgm"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值