【问题标题】:Having different values between Java and Matlab when Reading a .pgm image读取 .pgm 图像时在 Java 和 Matlab 之间具有不同的值
【发布时间】:2014-12-19 13:45:48
【问题描述】:

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

为此,我需要在 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 的图像: (对不起,由于缺乏声誉,我无法发布图片)

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

【问题讨论】:

  • 您的 java 实现正在打印文本字节“21 2 1”等的 ASCII 值。一些 PGM 文件使用文本标题,但像素本身的二进制表示。这些在开头用不同的魔术字符串标记。看起来 java 代码正在读取文件,就好像它有二进制像素一样。
  • 谢谢@Peter 我现在明白了。但是你能不能给我一个替代方法来读取整个整数并忽略空格。所以我可以在我的控制台上输出记事本++
  • @AliSmart - 只需查找 ASCII 码 32。如果您读到数字 32,请跳过并继续。

标签: java matlab matrix io pgm


【解决方案1】:

看看this post in particular。我在 imread 和 Java 的 ImageIO 类中遇到过类似的问题,而且在很长一段时间内,我都找不到这个链接来证明其他人也经历过同样的事情......直到现在。同样,有人遇到了相关问题in this post,但与您遇到的情况不太一样。

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

具体来说,您需要这样做:

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

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

替代方案...可能更适合您

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

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

【讨论】:

  • 非常感谢@rayryeng 先生,这真的是一个非常清楚的解释。但是如何在java中缩放像素强度以使最大255而不是在MATLAB中取消缩放它们。我认为这两种方式都适用于训练机器学习的分类模型。另一个注意事项是,如果您查看记事本图像中的第三个像素强度,它是 1,但 matlab 中的相应像素是 2,因此我认为 matlab 正在四舍五入 (255/156)*pixel 的值。我的理解有误吗?
  • 是的,如果您愿意,您可以这样做。确保您遵循 Peter 的建议,以便正确阅读它们,然后在 Java 中对其进行缩放,以便与 MATLAB 的功能相匹配。而且他们可能是四舍五入而不是地板,这就是为什么它是 2,所以我为这个错误道歉。
【解决方案2】:

您的 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());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    • 2016-07-25
    • 1970-01-01
    相关资源
    最近更新 更多