Skip to content

Commit 5aac909

Browse files
vinay0410alalek
authored andcommitted
Merge pull request opencv#10352 from vinay0410:write_pbm
* added write as pbm * add tests for pbm * imgcodecs: PBM support - drop additional PBM parameters - write: fix P1/P4 mode (no maxval 255 value after width/height) - write: invert values for P1/P4 - write: P1: compact ASCII mode (no spaces) - simplify pbm test - drop .pxm extension (http://netpbm.sourceforge.net/doc/ doesn't know such extension)
1 parent 592f8d8 commit 5aac909

File tree

4 files changed

+207
-62
lines changed

4 files changed

+207
-62
lines changed

modules/imgcodecs/src/grfmt_pxm.cpp

Lines changed: 131 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -374,31 +374,33 @@ bool PxMDecoder::readData( Mat& img )
374374

375375
//////////////////////////////////////////////////////////////////////////////////////////
376376

377-
PxMEncoder::PxMEncoder()
377+
PxMEncoder::PxMEncoder(PxMMode mode) :
378+
mode_(mode)
378379
{
379-
m_description = "Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)";
380+
switch (mode)
381+
{
382+
case PXM_TYPE_AUTO: m_description = "Portable image format - auto (*.pnm)"; break;
383+
case PXM_TYPE_PBM: m_description = "Portable image format - monochrome (*.pbm)"; break;
384+
case PXM_TYPE_PGM: m_description = "Portable image format - gray (*.pgm)"; break;
385+
case PXM_TYPE_PPM: m_description = "Portable image format - color (*.ppm)"; break;
386+
default:
387+
CV_Error(Error::StsInternal, "");
388+
}
380389
m_buf_supported = true;
381390
}
382391

383-
384392
PxMEncoder::~PxMEncoder()
385393
{
386394
}
387395

388-
389-
ImageEncoder PxMEncoder::newEncoder() const
390-
{
391-
return makePtr<PxMEncoder>();
392-
}
393-
394-
395-
bool PxMEncoder::isFormatSupported( int depth ) const
396+
bool PxMEncoder::isFormatSupported(int depth) const
396397
{
398+
if (mode_ == PXM_TYPE_PBM)
399+
return depth == CV_8U;
397400
return depth == CV_8U || depth == CV_16U;
398401
}
399402

400-
401-
bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
403+
bool PxMEncoder::write(const Mat& img, const std::vector<int>& params)
402404
{
403405
bool isBinary = true;
404406

@@ -409,8 +411,29 @@ bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
409411
int x, y;
410412

411413
for( size_t i = 0; i < params.size(); i += 2 )
412-
if( params[i] == CV_IMWRITE_PXM_BINARY )
414+
{
415+
if( params[i] == IMWRITE_PXM_BINARY )
413416
isBinary = params[i+1] != 0;
417+
}
418+
419+
int mode = mode_;
420+
if (mode == PXM_TYPE_AUTO)
421+
{
422+
mode = img.channels() == 1 ? PXM_TYPE_PGM : PXM_TYPE_PPM;
423+
}
424+
425+
if (mode == PXM_TYPE_PGM && img.channels() > 1)
426+
{
427+
CV_Error(Error::StsBadArg, "Portable bitmap(.pgm) expects gray image");
428+
}
429+
if (mode == PXM_TYPE_PPM && img.channels() != 3)
430+
{
431+
CV_Error(Error::StsBadArg, "Portable bitmap(.ppm) expects BGR image");
432+
}
433+
if (mode == PXM_TYPE_PBM && img.type() != CV_8UC1)
434+
{
435+
CV_Error(Error::StsBadArg, "For portable bitmap(.pbm) type must be CV_8UC1");
436+
}
414437

415438
WLByteStream strm;
416439

@@ -441,18 +464,58 @@ bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
441464
char* buffer = _buffer;
442465

443466
// write header;
444-
sprintf( buffer, "P%c\n# Generated by OpenCV %s\n%d %d\n%d\n",
445-
'2' + (channels > 1 ? 1 : 0) + (isBinary ? 3 : 0),
446-
CV_VERSION,
447-
width, height, (1 << depth) - 1 );
467+
const int code = ((mode == PXM_TYPE_PBM) ? 1 : (mode == PXM_TYPE_PGM) ? 2 : 3)
468+
+ (isBinary ? 3 : 0);
469+
const char* comment = "# Generated by OpenCV " CV_VERSION "\n";
470+
471+
int header_sz = sprintf(buffer, "P%c\n%s%d %d\n",
472+
(char)('0' + code), comment,
473+
width, height);
474+
CV_Assert(header_sz > 0);
475+
if (mode != PXM_TYPE_PBM)
476+
{
477+
int sz = sprintf(&buffer[header_sz], "%d\n", (1 << depth) - 1);
478+
CV_Assert(sz > 0);
479+
header_sz += sz;
480+
}
448481

449-
strm.putBytes( buffer, (int)strlen(buffer) );
482+
strm.putBytes(buffer, header_sz);
450483

451484
for( y = 0; y < height; y++ )
452485
{
453486
const uchar* const data = img.ptr(y);
454487
if( isBinary )
455488
{
489+
if (mode == PXM_TYPE_PBM)
490+
{
491+
char* ptr = buffer;
492+
int bcount = 7;
493+
char byte = 0;
494+
for (x = 0; x < width; x++)
495+
{
496+
if (bcount == 0)
497+
{
498+
if (data[x] == 0)
499+
byte = (byte) | 1;
500+
*ptr++ = byte;
501+
bcount = 7;
502+
byte = 0;
503+
}
504+
else
505+
{
506+
if (data[x] == 0)
507+
byte = (byte) | (1 << bcount);
508+
bcount--;
509+
}
510+
}
511+
if (bcount != 7)
512+
{
513+
*ptr++ = byte;
514+
}
515+
strm.putBytes(buffer, (int)(ptr - buffer));
516+
continue;
517+
}
518+
456519
if( _channels == 3 )
457520
{
458521
if( depth == 8 )
@@ -475,59 +538,72 @@ bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
475538
buffer[x + 1] = v;
476539
}
477540
}
478-
strm.putBytes( (channels > 1 || depth > 8) ? buffer : (const char*)data, fileStep );
541+
542+
strm.putBytes( (channels > 1 || depth > 8) ? buffer : (const char*)data, fileStep);
479543
}
480544
else
481545
{
482546
char* ptr = buffer;
483-
484-
if( channels > 1 )
547+
if (mode == PXM_TYPE_PBM)
485548
{
486-
if( depth == 8 )
549+
CV_Assert(channels == 1);
550+
CV_Assert(depth == 8);
551+
for (x = 0; x < width; x++)
487552
{
488-
for( x = 0; x < width*channels; x += channels )
489-
{
490-
sprintf( ptr, "% 4d", data[x + 2] );
491-
ptr += 4;
492-
sprintf( ptr, "% 4d", data[x + 1] );
493-
ptr += 4;
494-
sprintf( ptr, "% 4d", data[x] );
495-
ptr += 4;
496-
*ptr++ = ' ';
497-
*ptr++ = ' ';
498-
}
499-
}
500-
else
501-
{
502-
for( x = 0; x < width*channels; x += channels )
503-
{
504-
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 2] );
505-
ptr += 6;
506-
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 1] );
507-
ptr += 6;
508-
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
509-
ptr += 6;
510-
*ptr++ = ' ';
511-
*ptr++ = ' ';
512-
}
553+
ptr[0] = data[x] ? '0' : '1';
554+
ptr += 1;
513555
}
514556
}
515557
else
516558
{
517-
if( depth == 8 )
559+
if( channels > 1 )
518560
{
519-
for( x = 0; x < width; x++ )
561+
if( depth == 8 )
562+
{
563+
for( x = 0; x < width*channels; x += channels )
564+
{
565+
sprintf( ptr, "% 4d", data[x + 2] );
566+
ptr += 4;
567+
sprintf( ptr, "% 4d", data[x + 1] );
568+
ptr += 4;
569+
sprintf( ptr, "% 4d", data[x] );
570+
ptr += 4;
571+
*ptr++ = ' ';
572+
*ptr++ = ' ';
573+
}
574+
}
575+
else
520576
{
521-
sprintf( ptr, "% 4d", data[x] );
522-
ptr += 4;
577+
for( x = 0; x < width*channels; x += channels )
578+
{
579+
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 2] );
580+
ptr += 6;
581+
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 1] );
582+
ptr += 6;
583+
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
584+
ptr += 6;
585+
*ptr++ = ' ';
586+
*ptr++ = ' ';
587+
}
523588
}
524589
}
525590
else
526591
{
527-
for( x = 0; x < width; x++ )
592+
if( depth == 8 )
528593
{
529-
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
530-
ptr += 6;
594+
for( x = 0; x < width; x++ )
595+
{
596+
sprintf( ptr, "% 4d", data[x] );
597+
ptr += 4;
598+
}
599+
}
600+
else
601+
{
602+
for( x = 0; x < width; x++ )
603+
{
604+
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
605+
ptr += 6;
606+
}
531607
}
532608
}
533609
}

modules/imgcodecs/src/grfmt_pxm.hpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@
4949
namespace cv
5050
{
5151

52+
enum PxMMode
53+
{
54+
PXM_TYPE_AUTO = 0, // "auto"
55+
PXM_TYPE_PBM = 1, // monochrome format (single channel)
56+
PXM_TYPE_PGM = 2, // gray format (single channel)
57+
PXM_TYPE_PPM = 3 // color format
58+
};
59+
5260
class PxMDecoder : public BaseImageDecoder
5361
{
5462
public:
@@ -74,17 +82,21 @@ class PxMDecoder : public BaseImageDecoder
7482
int m_maxval;
7583
};
7684

77-
7885
class PxMEncoder : public BaseImageEncoder
7986
{
8087
public:
81-
PxMEncoder();
88+
PxMEncoder(PxMMode mode);
8289
virtual ~PxMEncoder();
8390

8491
bool isFormatSupported( int depth ) const;
8592
bool write( const Mat& img, const std::vector<int>& params );
8693

87-
ImageEncoder newEncoder() const;
94+
ImageEncoder newEncoder() const
95+
{
96+
return makePtr<PxMEncoder>(mode_);
97+
}
98+
99+
const PxMMode mode_;
88100
};
89101

90102
}

modules/imgcodecs/src/loadsave.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,10 @@ struct ImageCodecInitializer
144144
decoders.push_back( makePtr<SunRasterDecoder>() );
145145
encoders.push_back( makePtr<SunRasterEncoder>() );
146146
decoders.push_back( makePtr<PxMDecoder>() );
147-
encoders.push_back( makePtr<PxMEncoder>() );
147+
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_AUTO) );
148+
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_PBM) );
149+
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_PGM) );
150+
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_PPM) );
148151
#ifdef HAVE_TIFF
149152
decoders.push_back( makePtr<TiffDecoder>() );
150153
encoders.push_back( makePtr<TiffEncoder>() );

0 commit comments

Comments
 (0)