mirror of
https://github.com/opencv/opencv.git
synced 2025-07-24 14:06:27 +08:00
Merge pull request #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)
This commit is contained in:
parent
592f8d8c1b
commit
5aac909046
@ -374,31 +374,33 @@ bool PxMDecoder::readData( Mat& img )
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PxMEncoder::PxMEncoder()
|
||||
PxMEncoder::PxMEncoder(PxMMode mode) :
|
||||
mode_(mode)
|
||||
{
|
||||
m_description = "Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)";
|
||||
switch (mode)
|
||||
{
|
||||
case PXM_TYPE_AUTO: m_description = "Portable image format - auto (*.pnm)"; break;
|
||||
case PXM_TYPE_PBM: m_description = "Portable image format - monochrome (*.pbm)"; break;
|
||||
case PXM_TYPE_PGM: m_description = "Portable image format - gray (*.pgm)"; break;
|
||||
case PXM_TYPE_PPM: m_description = "Portable image format - color (*.ppm)"; break;
|
||||
default:
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
m_buf_supported = true;
|
||||
}
|
||||
|
||||
|
||||
PxMEncoder::~PxMEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ImageEncoder PxMEncoder::newEncoder() const
|
||||
{
|
||||
return makePtr<PxMEncoder>();
|
||||
}
|
||||
|
||||
|
||||
bool PxMEncoder::isFormatSupported( int depth ) const
|
||||
bool PxMEncoder::isFormatSupported(int depth) const
|
||||
{
|
||||
if (mode_ == PXM_TYPE_PBM)
|
||||
return depth == CV_8U;
|
||||
return depth == CV_8U || depth == CV_16U;
|
||||
}
|
||||
|
||||
|
||||
bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
bool PxMEncoder::write(const Mat& img, const std::vector<int>& params)
|
||||
{
|
||||
bool isBinary = true;
|
||||
|
||||
@ -409,8 +411,29 @@ bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
int x, y;
|
||||
|
||||
for( size_t i = 0; i < params.size(); i += 2 )
|
||||
if( params[i] == CV_IMWRITE_PXM_BINARY )
|
||||
{
|
||||
if( params[i] == IMWRITE_PXM_BINARY )
|
||||
isBinary = params[i+1] != 0;
|
||||
}
|
||||
|
||||
int mode = mode_;
|
||||
if (mode == PXM_TYPE_AUTO)
|
||||
{
|
||||
mode = img.channels() == 1 ? PXM_TYPE_PGM : PXM_TYPE_PPM;
|
||||
}
|
||||
|
||||
if (mode == PXM_TYPE_PGM && img.channels() > 1)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Portable bitmap(.pgm) expects gray image");
|
||||
}
|
||||
if (mode == PXM_TYPE_PPM && img.channels() != 3)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Portable bitmap(.ppm) expects BGR image");
|
||||
}
|
||||
if (mode == PXM_TYPE_PBM && img.type() != CV_8UC1)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "For portable bitmap(.pbm) type must be CV_8UC1");
|
||||
}
|
||||
|
||||
WLByteStream strm;
|
||||
|
||||
@ -441,18 +464,58 @@ bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
char* buffer = _buffer;
|
||||
|
||||
// write header;
|
||||
sprintf( buffer, "P%c\n# Generated by OpenCV %s\n%d %d\n%d\n",
|
||||
'2' + (channels > 1 ? 1 : 0) + (isBinary ? 3 : 0),
|
||||
CV_VERSION,
|
||||
width, height, (1 << depth) - 1 );
|
||||
const int code = ((mode == PXM_TYPE_PBM) ? 1 : (mode == PXM_TYPE_PGM) ? 2 : 3)
|
||||
+ (isBinary ? 3 : 0);
|
||||
const char* comment = "# Generated by OpenCV " CV_VERSION "\n";
|
||||
|
||||
strm.putBytes( buffer, (int)strlen(buffer) );
|
||||
int header_sz = sprintf(buffer, "P%c\n%s%d %d\n",
|
||||
(char)('0' + code), comment,
|
||||
width, height);
|
||||
CV_Assert(header_sz > 0);
|
||||
if (mode != PXM_TYPE_PBM)
|
||||
{
|
||||
int sz = sprintf(&buffer[header_sz], "%d\n", (1 << depth) - 1);
|
||||
CV_Assert(sz > 0);
|
||||
header_sz += sz;
|
||||
}
|
||||
|
||||
strm.putBytes(buffer, header_sz);
|
||||
|
||||
for( y = 0; y < height; y++ )
|
||||
{
|
||||
const uchar* const data = img.ptr(y);
|
||||
if( isBinary )
|
||||
{
|
||||
if (mode == PXM_TYPE_PBM)
|
||||
{
|
||||
char* ptr = buffer;
|
||||
int bcount = 7;
|
||||
char byte = 0;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
if (bcount == 0)
|
||||
{
|
||||
if (data[x] == 0)
|
||||
byte = (byte) | 1;
|
||||
*ptr++ = byte;
|
||||
bcount = 7;
|
||||
byte = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data[x] == 0)
|
||||
byte = (byte) | (1 << bcount);
|
||||
bcount--;
|
||||
}
|
||||
}
|
||||
if (bcount != 7)
|
||||
{
|
||||
*ptr++ = byte;
|
||||
}
|
||||
strm.putBytes(buffer, (int)(ptr - buffer));
|
||||
continue;
|
||||
}
|
||||
|
||||
if( _channels == 3 )
|
||||
{
|
||||
if( depth == 8 )
|
||||
@ -475,59 +538,72 @@ bool PxMEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
buffer[x + 1] = v;
|
||||
}
|
||||
}
|
||||
strm.putBytes( (channels > 1 || depth > 8) ? buffer : (const char*)data, fileStep );
|
||||
|
||||
strm.putBytes( (channels > 1 || depth > 8) ? buffer : (const char*)data, fileStep);
|
||||
}
|
||||
else
|
||||
{
|
||||
char* ptr = buffer;
|
||||
|
||||
if( channels > 1 )
|
||||
if (mode == PXM_TYPE_PBM)
|
||||
{
|
||||
if( depth == 8 )
|
||||
CV_Assert(channels == 1);
|
||||
CV_Assert(depth == 8);
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
for( x = 0; x < width*channels; x += channels )
|
||||
{
|
||||
sprintf( ptr, "% 4d", data[x + 2] );
|
||||
ptr += 4;
|
||||
sprintf( ptr, "% 4d", data[x + 1] );
|
||||
ptr += 4;
|
||||
sprintf( ptr, "% 4d", data[x] );
|
||||
ptr += 4;
|
||||
*ptr++ = ' ';
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( x = 0; x < width*channels; x += channels )
|
||||
{
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 2] );
|
||||
ptr += 6;
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 1] );
|
||||
ptr += 6;
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
|
||||
ptr += 6;
|
||||
*ptr++ = ' ';
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
ptr[0] = data[x] ? '0' : '1';
|
||||
ptr += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( depth == 8 )
|
||||
if( channels > 1 )
|
||||
{
|
||||
for( x = 0; x < width; x++ )
|
||||
if( depth == 8 )
|
||||
{
|
||||
sprintf( ptr, "% 4d", data[x] );
|
||||
ptr += 4;
|
||||
for( x = 0; x < width*channels; x += channels )
|
||||
{
|
||||
sprintf( ptr, "% 4d", data[x + 2] );
|
||||
ptr += 4;
|
||||
sprintf( ptr, "% 4d", data[x + 1] );
|
||||
ptr += 4;
|
||||
sprintf( ptr, "% 4d", data[x] );
|
||||
ptr += 4;
|
||||
*ptr++ = ' ';
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( x = 0; x < width*channels; x += channels )
|
||||
{
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 2] );
|
||||
ptr += 6;
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 1] );
|
||||
ptr += 6;
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
|
||||
ptr += 6;
|
||||
*ptr++ = ' ';
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( x = 0; x < width; x++ )
|
||||
if( depth == 8 )
|
||||
{
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
|
||||
ptr += 6;
|
||||
for( x = 0; x < width; x++ )
|
||||
{
|
||||
sprintf( ptr, "% 4d", data[x] );
|
||||
ptr += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( x = 0; x < width; x++ )
|
||||
{
|
||||
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
|
||||
ptr += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,14 @@
|
||||
namespace cv
|
||||
{
|
||||
|
||||
enum PxMMode
|
||||
{
|
||||
PXM_TYPE_AUTO = 0, // "auto"
|
||||
PXM_TYPE_PBM = 1, // monochrome format (single channel)
|
||||
PXM_TYPE_PGM = 2, // gray format (single channel)
|
||||
PXM_TYPE_PPM = 3 // color format
|
||||
};
|
||||
|
||||
class PxMDecoder : public BaseImageDecoder
|
||||
{
|
||||
public:
|
||||
@ -74,17 +82,21 @@ protected:
|
||||
int m_maxval;
|
||||
};
|
||||
|
||||
|
||||
class PxMEncoder : public BaseImageEncoder
|
||||
{
|
||||
public:
|
||||
PxMEncoder();
|
||||
PxMEncoder(PxMMode mode);
|
||||
virtual ~PxMEncoder();
|
||||
|
||||
bool isFormatSupported( int depth ) const;
|
||||
bool write( const Mat& img, const std::vector<int>& params );
|
||||
|
||||
ImageEncoder newEncoder() const;
|
||||
ImageEncoder newEncoder() const
|
||||
{
|
||||
return makePtr<PxMEncoder>(mode_);
|
||||
}
|
||||
|
||||
const PxMMode mode_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -144,7 +144,10 @@ struct ImageCodecInitializer
|
||||
decoders.push_back( makePtr<SunRasterDecoder>() );
|
||||
encoders.push_back( makePtr<SunRasterEncoder>() );
|
||||
decoders.push_back( makePtr<PxMDecoder>() );
|
||||
encoders.push_back( makePtr<PxMEncoder>() );
|
||||
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_AUTO) );
|
||||
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_PBM) );
|
||||
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_PGM) );
|
||||
encoders.push_back( makePtr<PxMEncoder>(PXM_TYPE_PPM) );
|
||||
#ifdef HAVE_TIFF
|
||||
decoders.push_back( makePtr<TiffDecoder>() );
|
||||
encoders.push_back( makePtr<TiffEncoder>() );
|
||||
|
@ -144,16 +144,27 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq)
|
||||
for (int cn = 1; cn <= 4; cn++)
|
||||
{
|
||||
SCOPED_TRACE(format("channels %d", cn));
|
||||
std::vector<int> parameters;
|
||||
if (cn == 2)
|
||||
continue;
|
||||
if (cn == 4 && ext != ".tiff")
|
||||
continue;
|
||||
if (cn > 1 && (ext == ".pbm" || ext == ".pgm"))
|
||||
continue;
|
||||
if (cn != 3 && ext == ".ppm")
|
||||
continue;
|
||||
string filename = cv::tempfile(format("%d%s", cn, ext.c_str()).c_str());
|
||||
|
||||
Mat img_gt(size, CV_MAKETYPE(CV_8U, cn), Scalar::all(0));
|
||||
circle(img_gt, center, radius, Scalar::all(255));
|
||||
ASSERT_TRUE(imwrite(filename, img_gt));
|
||||
|
||||
#if 1
|
||||
if (ext == ".pbm" || ext == ".pgm" || ext == ".ppm")
|
||||
{
|
||||
parameters.push_back(IMWRITE_PXM_BINARY);
|
||||
parameters.push_back(0);
|
||||
}
|
||||
#endif
|
||||
ASSERT_TRUE(imwrite(filename, img_gt, parameters));
|
||||
Mat img = imread(filename, IMREAD_UNCHANGED);
|
||||
ASSERT_FALSE(img.empty());
|
||||
EXPECT_EQ(img.size(), img.size());
|
||||
@ -175,7 +186,13 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq)
|
||||
EXPECT_LT(n, 1.);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img, img_gt);
|
||||
}
|
||||
#if 0
|
||||
std::cout << filename << std::endl;
|
||||
imshow("loaded", img);
|
||||
waitKey(0);
|
||||
#else
|
||||
EXPECT_EQ(0, remove(filename.c_str()));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,8 +208,11 @@ const string all_exts[] =
|
||||
".jpg",
|
||||
#endif
|
||||
".bmp",
|
||||
".pam",
|
||||
".ppm",
|
||||
".pgm",
|
||||
".pam"
|
||||
".pbm",
|
||||
".pnm"
|
||||
};
|
||||
|
||||
vector<Size> all_sizes()
|
||||
@ -208,6 +228,40 @@ INSTANTIATE_TEST_CASE_P(All, Imgcodecs_ExtSize,
|
||||
testing::ValuesIn(all_exts),
|
||||
testing::ValuesIn(all_sizes())));
|
||||
|
||||
typedef testing::TestWithParam<bool> Imgcodecs_pbm;
|
||||
TEST_P(Imgcodecs_pbm, write_read)
|
||||
{
|
||||
bool binary = GetParam();
|
||||
const String ext = "pbm";
|
||||
const string full_name = cv::tempfile(ext.c_str());
|
||||
|
||||
Size size(640, 480);
|
||||
const Point2i center = Point2i(size.width / 2, size.height / 2);
|
||||
const int radius = std::min(size.height, size.width / 4);
|
||||
Mat image(size, CV_8UC1, Scalar::all(0));
|
||||
circle(image, center, radius, Scalar::all(255));
|
||||
|
||||
vector<int> pbm_params;
|
||||
pbm_params.push_back(IMWRITE_PXM_BINARY);
|
||||
pbm_params.push_back(binary);
|
||||
|
||||
imwrite( full_name, image, pbm_params );
|
||||
Mat loaded = imread(full_name, IMREAD_UNCHANGED);
|
||||
ASSERT_FALSE(loaded.empty());
|
||||
|
||||
EXPECT_EQ(0, cvtest::norm(loaded, image, NORM_INF));
|
||||
|
||||
FILE *f = fopen(full_name.c_str(), "rb");
|
||||
ASSERT_TRUE(f != NULL);
|
||||
ASSERT_EQ('P', getc(f));
|
||||
ASSERT_EQ('1' + (binary ? 3 : 0), getc(f));
|
||||
fclose(f);
|
||||
EXPECT_EQ(0, remove(full_name.c_str()));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(All, Imgcodecs_pbm, testing::Bool());
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
TEST(Imgcodecs_Bmp, read_rle8)
|
||||
|
Loading…
Reference in New Issue
Block a user