在阅读.pgm图像时,Java和Matlab之间具有不同的值

时间:2014-12-19 13:45:49

标签: java matlab matrix io pgm

我必须为我的机器学习课程准备一套Trainging套装,其中对于给定的脸部图像,它会给你一个代表头部侧面的答案(笔直,左,右,上)

为此,我需要在java中读取.pgm图像文件并将其像素存储在矩阵X的一行中,然后将该图像的相应正确答案存储在y向量中。最后,我将这两个数组保存在.mat文件中。

问题是当尝试从(P2 .pgm)图像读取像素值并将其打印到控制台时,他们不会使用matlab矩阵查看器给出相同的值。会有什么问题?

这是我的代码:

try{
    InputStream f = Main.class.getResourceAsStream("an2i_left_angry_open.pgm");
    BufferedReader d = new BufferedReader(new InputStreamReader(f));
    String magic = d.readLine();    // first line contains P2 or P5
    String line = d.readLine();     // second line contains height and width
    while (line.startsWith("#")) {  // ignoring comment lines
            line = d.readLine();
        }
    Scanner s = new Scanner(line);
        int width = s.nextInt();
        int height = s.nextInt();
        line = d.readLine();// third line contains maxVal
        s = new Scanner(line);
        int maxVal = s.nextInt();
        for(int i=0;i<30;i++) /* printing first 30 values from the image including spaces*/
            System.out.println((byte)d.read());

        } catch (EOFException eof) {
            eof.printStackTrace(System.out) ;
        }

这些是我得到的价值: 50 49 32 50 32 49 32 48 32 50 32 49 56 32 53 57

虽然这张照片确实来自MATLAB Viewer的图像: (抱歉,由于缺乏声誉,我无法发布图片)

http://i59.tinypic.com/izw45g.png

这是您通过notepad ++打开.pgm文件时找到的内容

http://i62.tinypic.com/2rnzszr.png

2 个答案:

答案 0 :(得分:1)

看看this post in particular。我在imread和Java ImageIO课程中遇到过类似的问题,并且在最长的时间内,我找不到此链接作为其他人经历过同样事情的证据。 .. 到现在。同样,有人遇到相关问题in this post,但您遇到的情况并不完全相同。

基本上,Java和MATLAB中加载的图像不同的原因是增强目的。 MATLAB缩放强度,使图像大部分为黑色。基本上,PGM中的最大强度会缩放到255,而其他强度会线性缩放以适应[0,255]的动态范围。例如,如果您的图片的PGM文件中的[0-100]的动态范围 将其加载到imread,则会缩放到[0-255]并且不是[0-100]的原始比例。因此,您必须在加载图像之前知道图像的最大强度值(通过自己扫描文件)。通过阅读文件的第三行很容易做到这一点。在您的情况下,这将是156。找到后,您需要缩放图像中的每个值,以便在读取之前将其重新缩放到原来的值。

要确认是这种情况,请查看图像中的第一个像素,其原始PGM文件中的强度为21。因此,MATLAB将扩展强度,以便:

scaled = round(val*(255/156));

val将是输入强度,scaled是输出强度。因此,如果val = 21,那么scaled将是:

scaled = round(21*(255/156)) = 34

在MATLAB中读取时,这与第一个像素匹配。类似地,第一行中的第六个像素,原始值是18.MATLAB会将其缩放为:

scaled = round(18*(255/156)) = 29

这再次与您在MATLAB中看到的相匹配。现在开始看模式?基本上,要撤消缩放,您需要乘以缩放因子的倒数。因此,假设A是您加载的图像,则需要执行以下操作:

A_scaled = uint8(double(A)*(max_value/255));

A_scaled是输出图像,max_value是PGM文件中加载imread之前找到的最大强度。当[0-255]缩放图像时,此撤消缩放。请注意,我需要先将图像转换为double,然后使用缩放系数进行乘法运算,因为这很可能会产生浮点值,然后重新投射返回{{1} }。因此,要将其恢复为uint8,您必须以相反的方式进行缩放。

特别是在您的情况下,您需要执行以下操作:

[0-max_value]

这里的缺点是你需要知道在处理图像之前最大值是多少,这会让人讨厌。一种可能性是使用MATLAB并实际使用文件指针打开文件并自己获取第三行的值。这也是一个烦人的步骤,但我有一个替代方案。

替代......可能对你更好

或者,这里有两个指向用MATLAB编写的函数的链接,这些函数读取和写入PGM文件没有进行不必要的缩放,并且它提供了你期望的结果(未缩放)。

read函数的工作原理是它使用文件指针打开图像并手动解析数据并将值存储到矩阵中。您可能希望使用此功能而不是依赖A_scaled = uint8(double(A)*(156/255)); 。为了保存图像,再次使用文件指针并写入值,以便保持PGM标准,并再次强调您的强度。

答案 1 :(得分:0)

您的java实现正在打印文本字节“21 2 1”等的ASCII值。

50->2
51->1
32->SPACE
50->2
32->SPACE
51->1 
etc.

某些PGM文件使用文本标题,但像素本身使用二进制表示。这些在开头标有不同的魔术字符串。看起来java代码正在读取文件,就好像它有二进制像素一样。

相反,您的PGM文件具有ASCII编码像素,您希望扫描每个像素的空格分隔值。您可以像读取宽度和高度一样进行此操作。

调试代码可能如下所示:

line = d.readLine(); // first image line
s = new Scanner(line);
for(int i=0;i<30;i++) /* printing first 30 values from the image including spaces*/
    System.out.println((byte)s.nextInt());