はじめに
この記事では、最大値255のPGM バイナリ(PNM P5)形式の画像を
cv::Mat
に読み込むコードを載せております。
しかし OpenCV はcv::imread
で上記の形式に限らず PNM 形式を読み込めますので、
本来このような処理を自分で書く必要はありません。
所謂、車輪の再発明です。
上記の形式ならば、あらかじめメモリを確保したcv::Mat
のdataメンバに直接readして
同じものになるのではないかという実験的なものです。
事前にメモリ確保すれば処理時間が短くなることが期待できるかもしれませんが、
参考にする際は上記のことをご留意ください。
コード
#include <opencv2/opencv.hpp> // 負の値は異常値 int32_t readPnmNumber(std::istream& is) { char c = -1; // 1文字目を見つける bool comment = false; while (is.get(c)) { if (comment) { if (c == '\n' || c == '\r') { comment = false; } } else if (c == '#') { comment = true; } else if (std::isspace(c)) { continue; } else { break; } } if (!std::isdigit(c)) { return -1; } std::string str(1, c); // 2文字目以降を追加する。 while (is.get(c)) { if (std::isspace(c)) { break; } if (!std::isdigit(c)) { return -1; } str.push_back(c); } // 文字列を数値に変換 char* ptr; auto n = std::strtoul(str.c_str(), &ptr, 10); assert((ptr - &*str.begin()) == str.size()); return n; } struct PnmHeader { char magic_number[2]; int width; int height; int maximum_value; }; bool readPnmHeader(std::istream& is, PnmHeader& pnm_header) { char c; // magic number if (!is.get(pnm_header.magic_number[0])) { return false; } if (!is.get(pnm_header.magic_number[1])) { return false; } if (!is.get(c)) { return false; } bool is_pnm = (pnm_header.magic_number[0] == 'P') && ('0' < pnm_header.magic_number[1] && pnm_header.magic_number[1] <= '6') && std::isspace(c); if (!is_pnm) { return false; } // width height pnm_header.width = readPnmNumber(is); pnm_header.height = readPnmNumber(is); is_pnm = pnm_header.width > 0 && pnm_header.height > 0; if (!is_pnm) { return false; } // maximum value if (!(pnm_header.magic_number[1] == '1' || pnm_header.magic_number[1] == '4')) { pnm_header.maximum_value = readPnmNumber(is); if (pnm_header.maximum_value < 0) { return false; } } return true; } bool readPnmP5Max255(const std::string& filename, cv::Mat& img) { std::ifstream ifs(filename, std::ios::binary); if (!ifs.is_open()) { return false; } PnmHeader header; bool reads = readPnmHeader(ifs, header); if (!reads) { return false; } if (header.magic_number[1] == '5' && header.maximum_value == 255) { img.create(header.height, header.width, CV_8UC1); char* p = reinterpret_cast<char*>(img.data); ifs.read(p, header.height * header.width); } else { return false; } return true; } int main() { std::string filename = "test.pgm"; cv::Mat1b img1; readPnmP5Max255(filename, img1); // 一致確認 auto img2 = cv::imread(filename, cv::IMREAD_GRAYSCALE); std::cout << cv::countNonZero(img - img2); return 0; }
結果(標準出力)
0
参考
PNM形式のヘッダの読み込み処理に関して、以下のページを参考にさせていただきました。 www.mm2d.net