一种简化PNM (PBM/PGM/PPM) 标头处理的方法是逐行构建标头字符串,直到您捕获所有必需的数据。仅使用标准 C++ 库来执行此操作不需要太多代码...
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
...
std::string header, magic;
int width=0, height=0, maxsample=0, samples=0, bits=0, bytes=0;
do {
try { getline(is,magic); } catch ( const std::ios_base::failure & ) {}
if ( !magic.empty() && magic[0] != '#' ) header += magic+" ";
if ( !( std::stringstream(header+" 1") >> magic >> width >> height >> maxsample ).eof() ) break;
if ( ( (magic=="P1"||magic=="P4") && maxsample==1 ) || !is.good() ) break;
} while ( true );
samples = magic=="P1"?1:magic=="P2"?1:magic=="P3"?3:magic=="P4"?1:magic=="P5"?1:magic=="P6"?3:0;
bits = (magic=="P1"||magic=="P4")?1:maxsample<256?8:maxsample<256*256?16:0, bytes = (width*samples*bits+7)>>3;
if ( width<=0 || height<=0 || maxsample<=0 || samples<=0 || bits<=0 ) throw std::runtime_error("invalid PNM header");
这处理注释(如果存在)和 PBM 的特殊情况(无“最大样本”)——无论输入流上是否启用了异常,它都可以工作。
一旦您阅读了标题,读取图像数据通常是一件简单的事情,因为格式被定义为只是一个顺序数据转储(根据“魔术”值可能是 ASCII 或二进制)。在 16 位二进制编码样本的情况下,格式规范表明“最高有效字节在前”(大端),因此这种情况可能需要一些特定于平台的处理。
正如所写,这需要 C++11——可能是由于我使用stringstream
临时的方式。
一个警告:在一种病态的情况下,这可能会在尝试读取无效标头时浪费大量时间/RAM——因为getline
调用本身并没有限制。有一个相对简单的解决方案(替换getline
为更强大的东西),但它需要更多的代码。
对于生产质量的应用程序,请考虑使用libnetpbm。