|
|
|
@ -56,7 +56,7 @@ |
|
|
|
|
#include "utils.hpp" |
|
|
|
|
#include "grfmt_pam.hpp" |
|
|
|
|
|
|
|
|
|
using namespace cv; |
|
|
|
|
namespace cv { |
|
|
|
|
|
|
|
|
|
/* the PAM related fields */ |
|
|
|
|
#define MAX_PAM_HEADER_IDENITFIER_LENGTH 8 |
|
|
|
@ -220,14 +220,14 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool ReadPAMHeaderLine (cv::RLByteStream& strm, |
|
|
|
|
PamHeaderFieldType &fieldtype, |
|
|
|
|
char value[MAX_PAM_HEADER_VALUE_LENGTH+1]) |
|
|
|
|
static |
|
|
|
|
bool ReadPAMHeaderLine( |
|
|
|
|
cv::RLByteStream& strm, |
|
|
|
|
CV_OUT PamHeaderFieldType &fieldtype, |
|
|
|
|
CV_OUT char value[MAX_PAM_HEADER_VALUE_LENGTH+1]) |
|
|
|
|
{ |
|
|
|
|
int code, pos; |
|
|
|
|
bool ident_found = false; |
|
|
|
|
uint i; |
|
|
|
|
char ident[MAX_PAM_HEADER_IDENITFIER_LENGTH+1] = { 0 }; |
|
|
|
|
int code; |
|
|
|
|
char ident[MAX_PAM_HEADER_IDENITFIER_LENGTH+1] = {}; |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
code = strm.getByte(); |
|
|
|
@ -246,82 +246,95 @@ static bool ReadPAMHeaderLine (cv::RLByteStream& strm, |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* nul-ify buffers before writing to them */ |
|
|
|
|
memset (ident, '\0', sizeof(char) * MAX_PAM_HEADER_IDENITFIER_LENGTH); |
|
|
|
|
for (i=0; i<MAX_PAM_HEADER_IDENITFIER_LENGTH; i++) { |
|
|
|
|
if (!isspace(code)) |
|
|
|
|
ident[i] = (char) code; |
|
|
|
|
else |
|
|
|
|
int ident_sz = 0; |
|
|
|
|
for (; ident_sz < MAX_PAM_HEADER_IDENITFIER_LENGTH; ident_sz++) |
|
|
|
|
{ |
|
|
|
|
if (isspace(code)) |
|
|
|
|
break; |
|
|
|
|
ident[ident_sz] = (char)code; |
|
|
|
|
code = strm.getByte(); |
|
|
|
|
} |
|
|
|
|
CV_DbgAssert(ident_sz <= MAX_PAM_HEADER_IDENITFIER_LENGTH); |
|
|
|
|
ident[ident_sz] = 0; |
|
|
|
|
|
|
|
|
|
/* we may have filled the buffer and still have data */ |
|
|
|
|
if (!isspace(code)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
for (i=0; i<PAM_FIELDS_NO; i++) { |
|
|
|
|
if (strncmp(fields[i].identifier, ident, MAX_PAM_HEADER_IDENITFIER_LENGTH+1) == 0) { |
|
|
|
|
bool ident_found = false; |
|
|
|
|
for (uint i = 0; i < PAM_FIELDS_NO; i++) |
|
|
|
|
{ |
|
|
|
|
if (0 == strncmp(fields[i].identifier, ident, std::min(ident_sz, MAX_PAM_HEADER_IDENITFIER_LENGTH) + 1)) |
|
|
|
|
{ |
|
|
|
|
fieldtype = fields[i].type; |
|
|
|
|
ident_found = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ident_found) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
memset (value, '\0', sizeof(char) * MAX_PAM_HEADER_VALUE_LENGTH); |
|
|
|
|
memset(value, 0, sizeof(char) * (MAX_PAM_HEADER_VALUE_LENGTH + 1)); |
|
|
|
|
/* we may have an identifier that has no value */ |
|
|
|
|
if (code == '\n' || code == '\r') |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
code = strm.getByte(); |
|
|
|
|
} while ( isspace(code) ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} while (isspace(code)); |
|
|
|
|
|
|
|
|
|
/* read identifier value */ |
|
|
|
|
for (i=0; i<MAX_PAM_HEADER_VALUE_LENGTH; i++) { |
|
|
|
|
if (code != '\n' && code != '\r') { |
|
|
|
|
value[i] = (char) code; |
|
|
|
|
} else if (code != '\n' || code != '\r') |
|
|
|
|
int value_sz = 0; |
|
|
|
|
for (; value_sz < MAX_PAM_HEADER_VALUE_LENGTH; value_sz++) |
|
|
|
|
{ |
|
|
|
|
if (code == '\n' || code == '\r') |
|
|
|
|
break; |
|
|
|
|
value[value_sz] = (char)code; |
|
|
|
|
code = strm.getByte(); |
|
|
|
|
} |
|
|
|
|
pos = i; |
|
|
|
|
CV_DbgAssert(value_sz <= MAX_PAM_HEADER_VALUE_LENGTH); |
|
|
|
|
value[value_sz] = 0; |
|
|
|
|
|
|
|
|
|
int pos = value_sz; |
|
|
|
|
|
|
|
|
|
/* should be terminated */ |
|
|
|
|
if (code != '\n' && code != '\r') |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
/* remove trailing white spaces */ |
|
|
|
|
while (pos >= 0 && isspace(value[pos])) |
|
|
|
|
value[pos--] = '\0'; |
|
|
|
|
while (--pos >= 0 && isspace(value[pos])) |
|
|
|
|
value[pos] = 0; |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool ParseNumber (char *str, int *retval) |
|
|
|
|
static int ParseInt(const char *str, int len) |
|
|
|
|
{ |
|
|
|
|
char *endptr; |
|
|
|
|
long lval = strtol (str, &endptr, 0); |
|
|
|
|
|
|
|
|
|
if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) |
|
|
|
|
|| (errno != 0 && lval == 0)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
if (endptr == str) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
*retval = (int) lval; |
|
|
|
|
CV_Assert(len > 0); |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
int pos = 0; |
|
|
|
|
bool is_negative = false; |
|
|
|
|
if (str[0] == '-') |
|
|
|
|
{ |
|
|
|
|
is_negative = true; |
|
|
|
|
pos++; |
|
|
|
|
CV_Assert(isdigit(str[pos])); |
|
|
|
|
} |
|
|
|
|
uint64_t number = 0; |
|
|
|
|
while (pos < len && isdigit(str[pos])) |
|
|
|
|
{ |
|
|
|
|
char ch = str[pos]; |
|
|
|
|
number = (number * 10) + (uint64_t)((int)ch - (int)'0'); |
|
|
|
|
CV_Assert(number < INT_MAX); |
|
|
|
|
pos++; |
|
|
|
|
} |
|
|
|
|
if (pos < len) |
|
|
|
|
CV_Assert(str[pos] == 0); |
|
|
|
|
return (is_negative) ? -(int)number : (int)number; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
namespace cv |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PAMDecoder::PAMDecoder() |
|
|
|
|
{ |
|
|
|
@ -357,21 +370,12 @@ ImageDecoder PAMDecoder::newDecoder() const |
|
|
|
|
return makePtr<PAMDecoder>(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct parsed_fields |
|
|
|
|
{ |
|
|
|
|
bool endhdr, height, width, depth, maxval; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#define HEADER_READ_CORRECT(pf) (pf.endhdr && pf.height && pf.width \ |
|
|
|
|
&& pf.depth && pf.maxval) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool PAMDecoder::readHeader() |
|
|
|
|
bool PAMDecoder::readHeader() |
|
|
|
|
{ |
|
|
|
|
PamHeaderFieldType fieldtype = PAM_HEADER_NONE; |
|
|
|
|
char value[MAX_PAM_HEADER_VALUE_LENGTH+1]; |
|
|
|
|
int byte; |
|
|
|
|
struct parsed_fields flds; |
|
|
|
|
|
|
|
|
|
if( !m_buf.empty() ) |
|
|
|
|
{ |
|
|
|
|
if( !m_strm.open(m_buf) ) |
|
|
|
@ -379,6 +383,7 @@ bool PAMDecoder::readHeader() |
|
|
|
|
} |
|
|
|
|
else if( !m_strm.open( m_filename )) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
byte = m_strm.getByte(); |
|
|
|
@ -393,70 +398,72 @@ bool PAMDecoder::readHeader() |
|
|
|
|
if (byte != '\n' && byte != '\r') |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
|
|
|
|
|
uint i; |
|
|
|
|
memset (&flds, 0x00, sizeof (struct parsed_fields)); |
|
|
|
|
bool flds_endhdr = false, flds_height = false, flds_width = false, flds_depth = false, flds_maxval = false; |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
if (!ReadPAMHeaderLine(m_strm, fieldtype, value)) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
switch (fieldtype) { |
|
|
|
|
switch (fieldtype) |
|
|
|
|
{ |
|
|
|
|
case PAM_HEADER_NONE: |
|
|
|
|
case PAM_HEADER_COMMENT: |
|
|
|
|
continue; |
|
|
|
|
case PAM_HEADER_ENDHDR: |
|
|
|
|
flds.endhdr = true; |
|
|
|
|
flds_endhdr = true; |
|
|
|
|
break; |
|
|
|
|
case PAM_HEADER_HEIGHT: |
|
|
|
|
if (flds.height) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
if (!ParseNumber (value, &m_height)) |
|
|
|
|
if (flds_height) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
flds.height = true; |
|
|
|
|
m_height = ParseInt(value, MAX_PAM_HEADER_VALUE_LENGTH); |
|
|
|
|
flds_height = true; |
|
|
|
|
break; |
|
|
|
|
case PAM_HEADER_WIDTH: |
|
|
|
|
if (flds.width) |
|
|
|
|
if (flds_width) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
if (!ParseNumber (value, &m_width)) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
flds.width = true; |
|
|
|
|
m_width = ParseInt(value, MAX_PAM_HEADER_VALUE_LENGTH); |
|
|
|
|
flds_width = true; |
|
|
|
|
break; |
|
|
|
|
case PAM_HEADER_DEPTH: |
|
|
|
|
if (flds.depth) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
if (!ParseNumber (value, &m_channels)) |
|
|
|
|
if (flds_depth) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
flds.depth = true; |
|
|
|
|
m_channels = ParseInt(value, MAX_PAM_HEADER_VALUE_LENGTH); |
|
|
|
|
flds_depth = true; |
|
|
|
|
break; |
|
|
|
|
case PAM_HEADER_MAXVAL: |
|
|
|
|
if (flds.maxval) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
if (!ParseNumber (value, &m_maxval)) |
|
|
|
|
if (flds_maxval) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
m_maxval = ParseInt(value, MAX_PAM_HEADER_VALUE_LENGTH); |
|
|
|
|
if ( m_maxval > 65535 ) |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
if ( m_maxval > 255 ) { |
|
|
|
|
m_sampledepth = CV_16U; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
m_sampledepth = CV_8U; |
|
|
|
|
m_sampledepth = (m_maxval > 255) ? CV_16U : CV_8U; |
|
|
|
|
if (m_maxval == 1) |
|
|
|
|
bit_mode = true; |
|
|
|
|
flds.maxval = true; |
|
|
|
|
flds_maxval = true; |
|
|
|
|
break; |
|
|
|
|
case PAM_HEADER_TUPLTYPE: |
|
|
|
|
for (i=0; i<PAM_FORMATS_NO; i++) { |
|
|
|
|
if (strncmp(formats[i].name, |
|
|
|
|
value, MAX_PAM_HEADER_VALUE_LENGTH+1) == 0) { |
|
|
|
|
{ |
|
|
|
|
bool format_found = false; |
|
|
|
|
for (uint i=0; i<PAM_FORMATS_NO; i++) |
|
|
|
|
{ |
|
|
|
|
if (0 == strncmp(formats[i].name, value, MAX_PAM_HEADER_VALUE_LENGTH+1)) |
|
|
|
|
{ |
|
|
|
|
selected_fmt = formats[i].fmt; |
|
|
|
|
format_found = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
CV_Assert(format_found); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
throw RBS_BAD_HEADER; |
|
|
|
|
} |
|
|
|
|
} while (fieldtype != PAM_HEADER_ENDHDR); |
|
|
|
|
|
|
|
|
|
if (HEADER_READ_CORRECT(flds)) { |
|
|
|
|
if (selected_fmt == CV_IMWRITE_PAM_FORMAT_NULL) { |
|
|
|
|
if (flds_endhdr && flds_height && flds_width && flds_depth && flds_maxval) |
|
|
|
|
{ |
|
|
|
|
if (selected_fmt == CV_IMWRITE_PAM_FORMAT_NULL) |
|
|
|
|
{ |
|
|
|
|
if (m_channels == 1 && m_maxval == 1) |
|
|
|
|
selected_fmt = CV_IMWRITE_PAM_FORMAT_BLACKANDWHITE; |
|
|
|
|
else if (m_channels == 1 && m_maxval < 256) |
|
|
|
@ -469,28 +476,32 @@ bool PAMDecoder::readHeader() |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} catch(...) |
|
|
|
|
|
|
|
|
|
// failed
|
|
|
|
|
m_offset = -1; |
|
|
|
|
m_width = m_height = -1; |
|
|
|
|
m_strm.close(); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
catch (...) |
|
|
|
|
{ |
|
|
|
|
m_offset = -1; |
|
|
|
|
m_width = m_height = -1; |
|
|
|
|
m_strm.close(); |
|
|
|
|
throw; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
m_offset = -1; |
|
|
|
|
m_width = m_height = -1; |
|
|
|
|
m_strm.close(); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
bool PAMDecoder::readData(Mat& img) |
|
|
|
|
{ |
|
|
|
|
uchar* data = img.ptr(); |
|
|
|
|
int target_channels = img.channels(); |
|
|
|
|
const int target_channels = img.channels(); |
|
|
|
|
size_t imp_stride = img.step; |
|
|
|
|
int sample_depth = CV_ELEM_SIZE1(m_type); |
|
|
|
|
int src_elems_per_row = m_width*m_channels; |
|
|
|
|
int src_stride = src_elems_per_row*sample_depth; |
|
|
|
|
int x, y; |
|
|
|
|
bool res = false, funcout; |
|
|
|
|
PaletteEntry palette[256]; |
|
|
|
|
const int sample_depth = CV_ELEM_SIZE1(m_type); |
|
|
|
|
const int src_elems_per_row = m_width*m_channels; |
|
|
|
|
const int src_stride = src_elems_per_row*sample_depth; |
|
|
|
|
PaletteEntry palette[256] = {}; |
|
|
|
|
const struct pam_format *fmt = NULL; |
|
|
|
|
struct channel_layout layout = { 0, 0, 0, 0 }; // normalized to 1-channel grey format
|
|
|
|
|
|
|
|
|
@ -512,7 +523,6 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
m_strm.setPos( m_offset ); |
|
|
|
|
|
|
|
|
@ -521,10 +531,10 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
/* special case for 16bit images with wrong endianness */ |
|
|
|
|
if (m_sampledepth == CV_16U && !isBigEndian()) |
|
|
|
|
{ |
|
|
|
|
for (y = 0; y < m_height; y++, data += imp_stride ) |
|
|
|
|
for (int y = 0; y < m_height; y++, data += imp_stride) |
|
|
|
|
{ |
|
|
|
|
m_strm.getBytes( src, src_stride ); |
|
|
|
|
for( x = 0; x < src_elems_per_row; x++ ) |
|
|
|
|
for (int x = 0; x < src_elems_per_row; x++) |
|
|
|
|
{ |
|
|
|
|
uchar v = src[x * 2]; |
|
|
|
|
data[x * 2] = src[x * 2 + 1]; |
|
|
|
@ -543,7 +553,7 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
if( target_channels == 1 ) |
|
|
|
|
{ |
|
|
|
|
uchar gray_palette[2] = {0, 255}; |
|
|
|
|
for( y = 0; y < m_height; y++, data += imp_stride ) |
|
|
|
|
for (int y = 0; y < m_height; y++, data += imp_stride) |
|
|
|
|
{ |
|
|
|
|
m_strm.getBytes( src, src_stride ); |
|
|
|
|
FillGrayRow1( data, src, m_width, gray_palette ); |
|
|
|
@ -551,21 +561,21 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
} else if ( target_channels == 3 ) |
|
|
|
|
{ |
|
|
|
|
FillGrayPalette( palette, 1 , false ); |
|
|
|
|
for( y = 0; y < m_height; y++, data += imp_stride ) |
|
|
|
|
for (int y = 0; y < m_height; y++, data += imp_stride) |
|
|
|
|
{ |
|
|
|
|
m_strm.getBytes( src, src_stride ); |
|
|
|
|
FillColorRow1( data, src, m_width, palette ); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (y = 0; y < m_height; y++, data += imp_stride ) |
|
|
|
|
for (int y = 0; y < m_height; y++, data += imp_stride) |
|
|
|
|
{ |
|
|
|
|
m_strm.getBytes( src, src_stride ); |
|
|
|
|
|
|
|
|
|
/* endianness correction */ |
|
|
|
|
if( m_sampledepth == CV_16U && !isBigEndian() ) |
|
|
|
|
{ |
|
|
|
|
for( x = 0; x < src_elems_per_row; x++ ) |
|
|
|
|
for (int x = 0; x < src_elems_per_row; x++) |
|
|
|
|
{ |
|
|
|
|
uchar v = src[x * 2]; |
|
|
|
|
src[x * 2] = src[x * 2 + 1]; |
|
|
|
@ -576,7 +586,7 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
/* scale down */ |
|
|
|
|
if( img.depth() == CV_8U && m_sampledepth == CV_16U ) |
|
|
|
|
{ |
|
|
|
|
for( x = 0; x < src_elems_per_row; x++ ) |
|
|
|
|
for (int x = 0; x < src_elems_per_row; x++) |
|
|
|
|
{ |
|
|
|
|
int v = ((ushort *)src)[x]; |
|
|
|
|
src[x] = (uchar)(v >> 8); |
|
|
|
@ -589,7 +599,7 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
} |
|
|
|
|
/* perform correct conversion based on format */ |
|
|
|
|
else if (fmt) { |
|
|
|
|
funcout = false; |
|
|
|
|
bool funcout = false; |
|
|
|
|
if (fmt->cvt_func) |
|
|
|
|
funcout = fmt->cvt_func (src, data, m_width, target_channels, |
|
|
|
|
img.depth()); |
|
|
|
@ -608,13 +618,8 @@ bool PAMDecoder::readData( Mat& img ) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res = true; |
|
|
|
|
} catch(...) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return res; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|