0

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

为此,我需要在 java 中读取 .pgm 图像文件并将其像素存储在矩阵 X 的一行中,然后将该图像的适当正确答案存储在 ay 向量中。最后,我会将这两个数组保存在 .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 的图像:(抱歉,由于缺乏声誉,我无法发布图像)

这就是您通过记事本++ 打开 .pgm 文件时发现的内容

4

2 回答 2

1

尤其看一下这个帖子。我在imreadJava 的ImageIO课程中​​遇到过类似的问题,并且在很长一段时间内,我都找不到这个链接来证明其他人也经历过同样的事情......直到现在。同样,有人在这篇文章中遇到了相关问题,但与您遇到的情况并不完全相同。

本质上,Java 和 MATLAB 加载的图像不同的原因是出于增强目的。MATLAB 缩放强度,因此图像大部分不是黑色的。本质上,PGM 中的最大强度被缩放到255,而其他强度被线性缩放以适应[0,255]. 因此,例如,如果您图像[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使用imread. 这会撤消缩放,因为 MATLAB 从 缩放图像[0-255]。请注意,我需要double先将图像转换为,与缩放因子相乘,因为这很可能会产生浮点值,然后重新转换回uint8. 因此,要将其恢复为[0-max_value],您必须以相反的方式进行缩放。

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

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

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

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

或者,这里有两个指向用 MATLAB 编写的函数的链接,它们可以读取和写入 PGM 文件,而无需进行不必要的缩放,它将提供您期望的结果(未缩放)。

读取函数的工作原理是它使用文件指针打开图像并手动解析数据并将值存储到矩阵中。您可能想要使用此功能而不是依赖imread. 为了保存图像,再次使用文件指针并写入值以保持 PGM 标准,并且再次,您的强度未缩放。

于 2014-12-19T15:06:39.747 回答
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());
于 2014-12-19T20:58:45.093 回答