Path: blob/master/modules/imgcodecs/src/grfmt_pam.cpp
16337 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11// (3-clause BSD License)12//13// Copyright (C) 2000-2016, Intel Corporation, all rights reserved.14// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.15// Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.16// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.17// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.18// Copyright (C) 2015-2016, Itseez Inc., all rights reserved.19// Third party copyrights are property of their respective owners.20//21// Redistribution and use in source and binary forms, with or without modification,22// are permitted provided that the following conditions are met:23//24// * Redistributions of source code must retain the above copyright notice,25// this list of conditions and the following disclaimer.26//27// * Redistributions in binary form must reproduce the above copyright notice,28// this list of conditions and the following disclaimer in the documentation29// and/or other materials provided with the distribution.30//31// * Neither the names of the copyright holders nor the names of the contributors32// may be used to endorse or promote products derived from this software33// without specific prior written permission.34//35// This software is provided by the copyright holders and contributors "as is" and36// any express or implied warranties, including, but not limited to, the implied37// warranties of merchantability and fitness for a particular purpose are disclaimed.38// In no event shall copyright holders or contributors be liable for any direct,39// indirect, incidental, special, exemplary, or consequential damages40// (including, but not limited to, procurement of substitute goods or services;41// loss of use, data, or profits; or business interruption) however caused42// and on any theory of liability, whether in contract, strict liability,43// or tort (including negligence or otherwise) arising in any way out of44// the use of this software, even if advised of the possibility of such damage.45//46//M*/4748#include "precomp.hpp"495051#ifdef HAVE_IMGCODEC_PXM5253#include <cerrno>5455#include "utils.hpp"56#include "grfmt_pam.hpp"5758using namespace cv;5960/* the PAM related fields */61#define MAX_PAM_HEADER_IDENITFIER_LENGTH 862#define MAX_PAM_HEADER_VALUE_LENGTH 2556364/* PAM header fileds */65typedef enum {66PAM_HEADER_NONE,67PAM_HEADER_COMMENT,68PAM_HEADER_ENDHDR,69PAM_HEADER_HEIGHT,70PAM_HEADER_WIDTH,71PAM_HEADER_DEPTH,72PAM_HEADER_MAXVAL,73PAM_HEADER_TUPLTYPE,74} PamHeaderFieldType;7576struct pam_header_field {77PamHeaderFieldType type;78char identifier[MAX_PAM_HEADER_IDENITFIER_LENGTH+1];79};8081const static struct pam_header_field fields[] = {82{PAM_HEADER_ENDHDR, "ENDHDR"},83{PAM_HEADER_HEIGHT, "HEIGHT"},84{PAM_HEADER_WIDTH, "WIDTH"},85{PAM_HEADER_DEPTH, "DEPTH"},86{PAM_HEADER_MAXVAL, "MAXVAL"},87{PAM_HEADER_TUPLTYPE, "TUPLTYPE"},88};89#define PAM_FIELDS_NO (sizeof (fields) / sizeof ((fields)[0]))9091typedef bool (*cvtFunc) (void *src, void *target, int width, int target_channels,92int target_depth);9394struct channel_layout {95uint rchan, gchan, bchan, graychan;96};9798struct pam_format {99uint fmt;100char name[MAX_PAM_HEADER_VALUE_LENGTH+1];101cvtFunc cvt_func;102/* the channel layout that should be used when103* imread_ creates a 3 channel or 1 channel image104* used when no conversion function is available105*/106struct channel_layout layout;107};108109static bool rgb_convert (void *src, void *target, int width, int target_channels,110int target_depth);111112const static struct pam_format formats[] = {113{CV_IMWRITE_PAM_FORMAT_NULL, "", NULL, {0, 0, 0, 0} },114{CV_IMWRITE_PAM_FORMAT_BLACKANDWHITE, "BLACKANDWHITE", NULL, {0, 0, 0, 0} },115{CV_IMWRITE_PAM_FORMAT_GRAYSCALE, "GRAYSCALE", NULL, {0, 0, 0, 0} },116{CV_IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA, "GRAYSCALE_ALPHA", NULL, {0, 0, 0, 0} },117{CV_IMWRITE_PAM_FORMAT_RGB, "RGB", rgb_convert, {0, 1, 2, 0} },118{CV_IMWRITE_PAM_FORMAT_RGB_ALPHA, "RGB_ALPHA", NULL, {0, 1, 2, 0} },119};120#define PAM_FORMATS_NO (sizeof (fields) / sizeof ((fields)[0]))121122/*123* conversion functions124*/125126static bool127rgb_convert (void *src, void *target, int width, int target_channels, int target_depth)128{129bool ret = false;130if (target_channels == 3) {131switch (target_depth) {132case CV_8U:133icvCvt_RGB2BGR_8u_C3R( (uchar*) src, 0, (uchar*) target, 0,134cvSize(width,1) );135ret = true;136break;137case CV_16U:138icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)target, 0,139cvSize(width,1) );140ret = true;141break;142default:143break;144}145} else if (target_channels == 1) {146switch (target_depth) {147case CV_8U:148icvCvt_BGR2Gray_8u_C3C1R( (uchar*) src, 0, (uchar*) target, 0,149cvSize(width,1), 2 );150ret = true;151break;152case CV_16U:153icvCvt_BGRA2Gray_16u_CnC1R( (ushort *)src, 0, (ushort *)target, 0,154cvSize(width,1), 3, 2 );155ret = true;156break;157default:158break;159}160}161return ret;162}163164/*165* copy functions used as a fall back for undefined formats166* or simpler conversion options167*/168169static void170basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_size,171int src_width, void *target, int target_channels, int target_depth)172{173switch (target_depth) {174case CV_8U:175{176uchar *d = (uchar *)target, *s = (uchar *)src,177*end = ((uchar *)src) + src_width;178switch (target_channels) {179case 1:180for( ; s < end; d += 3, s += src_sampe_size )181d[0] = d[1] = d[2] = s[layout->graychan];182break;183case 3:184for( ; s < end; d += 3, s += src_sampe_size ) {185d[0] = s[layout->bchan];186d[1] = s[layout->gchan];187d[2] = s[layout->rchan];188}189break;190default:191CV_Error(Error::StsInternal, "");192}193break;194}195case CV_16U:196{197ushort *d = (ushort *)target, *s = (ushort *)src,198*end = ((ushort *)src) + src_width;199switch (target_channels) {200case 1:201for( ; s < end; d += 3, s += src_sampe_size )202d[0] = d[1] = d[2] = s[layout->graychan];203break;204case 3:205for( ; s < end; d += 3, s += src_sampe_size ) {206d[0] = s[layout->bchan];207d[1] = s[layout->gchan];208d[2] = s[layout->rchan];209}210break;211default:212CV_Error(Error::StsInternal, "");213}214break;215}216default:217CV_Error(Error::StsInternal, "");218}219}220221222static bool ReadPAMHeaderLine (cv::RLByteStream& strm,223PamHeaderFieldType &fieldtype,224char value[MAX_PAM_HEADER_VALUE_LENGTH+1])225{226int code, pos;227bool ident_found = false;228uint i;229char ident[MAX_PAM_HEADER_IDENITFIER_LENGTH+1] = { 0 };230231do {232code = strm.getByte();233} while ( isspace(code) );234235if (code == '#') {236/* we are in a comment, eat characters until linebreak */237do238{239code = strm.getByte();240} while( code != '\n' && code != '\r' );241fieldtype = PAM_HEADER_COMMENT;242return true;243} else if (code == '\n' || code == '\r' ) {244fieldtype = PAM_HEADER_NONE;245return true;246}247248/* nul-ify buffers before writing to them */249memset (ident, '\0', sizeof(char) * MAX_PAM_HEADER_IDENITFIER_LENGTH);250for (i=0; i<MAX_PAM_HEADER_IDENITFIER_LENGTH; i++) {251if (!isspace(code))252ident[i] = (char) code;253else254break;255code = strm.getByte();256}257258/* we may have filled the buffer and still have data */259if (!isspace(code))260return false;261262for (i=0; i<PAM_FIELDS_NO; i++) {263if (strncmp(fields[i].identifier, ident, MAX_PAM_HEADER_IDENITFIER_LENGTH+1) == 0) {264fieldtype = fields[i].type;265ident_found = true;266}267}268269if (!ident_found)270return false;271272memset (value, '\0', sizeof(char) * MAX_PAM_HEADER_VALUE_LENGTH);273/* we may have an identifier that has no value */274if (code == '\n' || code == '\r')275return true;276277do {278code = strm.getByte();279} while ( isspace(code) );280281282283/* read identifier value */284for (i=0; i<MAX_PAM_HEADER_VALUE_LENGTH; i++) {285if (code != '\n' && code != '\r') {286value[i] = (char) code;287} else if (code != '\n' || code != '\r')288break;289code = strm.getByte();290}291pos = i;292293/* should be terminated */294if (code != '\n' && code != '\r')295return false;296297/* remove trailing white spaces */298while (pos >= 0 && isspace(value[pos]))299value[pos--] = '\0';300301return true;302}303304static bool ParseNumber (char *str, int *retval)305{306char *endptr;307long lval = strtol (str, &endptr, 0);308309if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))310|| (errno != 0 && lval == 0)) {311return false;312}313if (endptr == str) {314return false;315}316317*retval = (int) lval;318319return true;320}321322namespace cv323{324325PAMDecoder::PAMDecoder()326{327m_offset = -1;328m_buf_supported = true;329bit_mode = false;330selected_fmt = CV_IMWRITE_PAM_FORMAT_NULL;331m_maxval = 0;332m_channels = 0;333m_sampledepth = 0;334}335336337PAMDecoder::~PAMDecoder()338{339m_strm.close();340}341342size_t PAMDecoder::signatureLength() const343{344return 3;345}346347bool PAMDecoder::checkSignature( const String& signature ) const348{349return signature.size() >= 3 && signature[0] == 'P' &&350signature[1] == '7' &&351isspace(signature[2]);352}353354ImageDecoder PAMDecoder::newDecoder() const355{356return makePtr<PAMDecoder>();357}358359struct parsed_fields360{361bool endhdr, height, width, depth, maxval;362};363364#define HEADER_READ_CORRECT(pf) (pf.endhdr && pf.height && pf.width \365&& pf.depth && pf.maxval)366367368bool PAMDecoder::readHeader()369{370PamHeaderFieldType fieldtype = PAM_HEADER_NONE;371char value[MAX_PAM_HEADER_VALUE_LENGTH+1];372int byte;373struct parsed_fields flds;374if( !m_buf.empty() )375{376if( !m_strm.open(m_buf) )377return false;378}379else if( !m_strm.open( m_filename ))380return false;381CV_TRY382{383byte = m_strm.getByte();384if( byte != 'P' )385CV_THROW( RBS_BAD_HEADER );386387byte = m_strm.getByte();388if (byte != '7')389CV_THROW( RBS_BAD_HEADER );390391byte = m_strm.getByte();392if (byte != '\n' && byte != '\r')393CV_THROW( RBS_BAD_HEADER );394395uint i;396memset (&flds, 0x00, sizeof (struct parsed_fields));397do {398if (!ReadPAMHeaderLine(m_strm, fieldtype, value))399CV_THROW( RBS_BAD_HEADER );400switch (fieldtype) {401case PAM_HEADER_NONE:402case PAM_HEADER_COMMENT:403continue;404case PAM_HEADER_ENDHDR:405flds.endhdr = true;406break;407case PAM_HEADER_HEIGHT:408if (flds.height)409CV_THROW( RBS_BAD_HEADER );410if (!ParseNumber (value, &m_height))411CV_THROW( RBS_BAD_HEADER );412flds.height = true;413break;414case PAM_HEADER_WIDTH:415if (flds.width)416CV_THROW( RBS_BAD_HEADER );417if (!ParseNumber (value, &m_width))418CV_THROW( RBS_BAD_HEADER );419flds.width = true;420break;421case PAM_HEADER_DEPTH:422if (flds.depth)423CV_THROW( RBS_BAD_HEADER );424if (!ParseNumber (value, &m_channels))425CV_THROW( RBS_BAD_HEADER );426flds.depth = true;427break;428case PAM_HEADER_MAXVAL:429if (flds.maxval)430CV_THROW( RBS_BAD_HEADER );431if (!ParseNumber (value, &m_maxval))432CV_THROW( RBS_BAD_HEADER );433if ( m_maxval > 65535 )434CV_THROW( RBS_BAD_HEADER );435if ( m_maxval > 255 ) {436m_sampledepth = CV_16U;437}438else439m_sampledepth = CV_8U;440if (m_maxval == 1)441bit_mode = true;442flds.maxval = true;443break;444case PAM_HEADER_TUPLTYPE:445for (i=0; i<PAM_FORMATS_NO; i++) {446if (strncmp(formats[i].name,447value, MAX_PAM_HEADER_VALUE_LENGTH+1) == 0) {448selected_fmt = formats[i].fmt;449}450}451break;452default:453CV_THROW( RBS_BAD_HEADER );454}455} while (fieldtype != PAM_HEADER_ENDHDR);456457if (HEADER_READ_CORRECT(flds)) {458if (selected_fmt == CV_IMWRITE_PAM_FORMAT_NULL) {459if (m_channels == 1 && m_maxval == 1)460selected_fmt = CV_IMWRITE_PAM_FORMAT_BLACKANDWHITE;461else if (m_channels == 1 && m_maxval < 256)462selected_fmt = CV_IMWRITE_PAM_FORMAT_GRAYSCALE;463else if (m_channels == 3 && m_maxval < 256)464selected_fmt = CV_IMWRITE_PAM_FORMAT_RGB;465}466m_type = CV_MAKETYPE(m_sampledepth, m_channels);467m_offset = m_strm.getPos();468469return true;470}471} CV_CATCH_ALL472{473}474475m_offset = -1;476m_width = m_height = -1;477m_strm.close();478return false;479}480481482bool PAMDecoder::readData( Mat& img )483{484uchar* data = img.ptr();485int target_channels = img.channels();486size_t imp_stride = img.step;487int sample_depth = CV_ELEM_SIZE1(m_type);488int src_elems_per_row = m_width*m_channels;489int src_stride = src_elems_per_row*sample_depth;490int x, y;491bool res = false, funcout;492PaletteEntry palette[256];493const struct pam_format *fmt = NULL;494struct channel_layout layout = { 0, 0, 0, 0 }; // normalized to 1-channel grey format495496/* setting buffer to max data size so scaling up is possible */497AutoBuffer<uchar> _src(src_elems_per_row * 2);498uchar* src = _src.data();499500if( m_offset < 0 || !m_strm.isOpened())501return false;502503if (selected_fmt != CV_IMWRITE_PAM_FORMAT_NULL)504fmt = &formats[selected_fmt];505else {506/* default layout handling */507if (m_channels >= 3) {508layout.bchan = 0;509layout.gchan = 1;510layout.rchan = 2;511}512}513514CV_TRY515{516m_strm.setPos( m_offset );517518/* the case where data fits the opencv matrix */519if (m_sampledepth == img.depth() && target_channels == m_channels && !bit_mode) {520/* special case for 16bit images with wrong endianness */521if (m_sampledepth == CV_16U && !isBigEndian())522{523for (y = 0; y < m_height; y++, data += imp_stride )524{525m_strm.getBytes( src, src_stride );526for( x = 0; x < src_elems_per_row; x++ )527{528uchar v = src[x * 2];529data[x * 2] = src[x * 2 + 1];530data[x * 2 + 1] = v;531}532}533}534else {535m_strm.getBytes( data, src_stride * m_height );536}537538}539else {540/* black and white mode */541if (bit_mode) {542if( target_channels == 1 )543{544uchar gray_palette[2] = {0, 255};545for( y = 0; y < m_height; y++, data += imp_stride )546{547m_strm.getBytes( src, src_stride );548FillGrayRow1( data, src, m_width, gray_palette );549}550} else if ( target_channels == 3 )551{552FillGrayPalette( palette, 1 , false );553for( y = 0; y < m_height; y++, data += imp_stride )554{555m_strm.getBytes( src, src_stride );556FillColorRow1( data, src, m_width, palette );557}558}559} else {560for (y = 0; y < m_height; y++, data += imp_stride )561{562m_strm.getBytes( src, src_stride );563564/* endianness correction */565if( m_sampledepth == CV_16U && !isBigEndian() )566{567for( x = 0; x < src_elems_per_row; x++ )568{569uchar v = src[x * 2];570src[x * 2] = src[x * 2 + 1];571src[x * 2 + 1] = v;572}573}574575/* scale down */576if( img.depth() == CV_8U && m_sampledepth == CV_16U )577{578for( x = 0; x < src_elems_per_row; x++ )579{580int v = ((ushort *)src)[x];581src[x] = (uchar)(v >> 8);582}583}584585/* if we are only scaling up/down then we can then copy the data */586if (target_channels == m_channels) {587memcpy (data, src, imp_stride);588}589/* perform correct conversion based on format */590else if (fmt) {591funcout = false;592if (fmt->cvt_func)593funcout = fmt->cvt_func (src, data, m_width, target_channels,594img.depth());595/* fall back to default if there is no conversion function or it596* can't handle the specified characteristics597*/598if (!funcout)599basic_conversion (src, &fmt->layout, m_channels,600m_width, data, target_channels, img.depth());601602/* default to selecting the first available channels */603} else {604basic_conversion (src, &layout, m_channels,605m_width, data, target_channels, img.depth());606}607}608}609}610611res = true;612} CV_CATCH_ALL613{614}615616return res;617}618619620//////////////////////////////////////////////////////////////////////////////////////////621622PAMEncoder::PAMEncoder()623{624m_description = "Portable arbitrary format (*.pam)";625m_buf_supported = true;626}627628629PAMEncoder::~PAMEncoder()630{631}632633634ImageEncoder PAMEncoder::newEncoder() const635{636return makePtr<PAMEncoder>();637}638639640bool PAMEncoder::isFormatSupported( int depth ) const641{642return depth == CV_8U || depth == CV_16U;643}644645646bool PAMEncoder::write( const Mat& img, const std::vector<int>& params )647{648649WLByteStream strm;650651int width = img.cols, height = img.rows;652int stride = width*(int)img.elemSize();653const uchar* data = img.ptr();654const struct pam_format *fmt = NULL;655int x, y, tmp, bufsize = 256;656657/* parse save file type */658for( size_t i = 0; i < params.size(); i += 2 )659if( params[i] == CV_IMWRITE_PAM_TUPLETYPE ) {660if ( params[i+1] > CV_IMWRITE_PAM_FORMAT_NULL &&661params[i+1] < (int) PAM_FORMATS_NO)662fmt = &formats[params[i+1]];663}664665if( m_buf )666{667if( !strm.open(*m_buf) )668return false;669m_buf->reserve( alignSize(256 + stride*height, 256));670}671else if( !strm.open(m_filename) )672return false;673674tmp = width * (int)img.elemSize();675676if (bufsize < tmp)677bufsize = tmp;678679AutoBuffer<char> _buffer(bufsize);680char* buffer = _buffer.data();681682/* write header */683tmp = 0;684tmp += sprintf( buffer, "P7\n");685tmp += sprintf( buffer + tmp, "WIDTH %d\n", width);686tmp += sprintf( buffer + tmp, "HEIGHT %d\n", height);687tmp += sprintf( buffer + tmp, "DEPTH %d\n", img.channels());688tmp += sprintf( buffer + tmp, "MAXVAL %d\n", (1 << img.elemSize1()*8) - 1);689if (fmt)690tmp += sprintf( buffer + tmp, "TUPLTYPE %s\n", fmt->name );691sprintf( buffer + tmp, "ENDHDR\n" );692693strm.putBytes( buffer, (int)strlen(buffer) );694/* write data */695if (img.depth() == CV_8U)696strm.putBytes( data, stride*height );697else if (img.depth() == CV_16U) {698/* fix endianness */699if (!isBigEndian()) {700for( y = 0; y < height; y++ ) {701memcpy( buffer, img.ptr(y), stride );702for( x = 0; x < stride; x += 2 )703{704uchar v = buffer[x];705buffer[x] = buffer[x + 1];706buffer[x + 1] = v;707}708strm.putBytes( buffer, stride );709}710} else711strm.putBytes( data, stride*height );712} else713CV_Error(Error::StsInternal, "");714715strm.close();716return true;717}718719}720721#endif722723724