1/*M///////////////////////////////////////////////////////////////////////////////////////
2//
3// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4//
5// By downloading, copying, installing or using the software you agree to this license.
6// If you do not agree to this license, do not download, install,
7// copy or use the software.
8//
9//
10// License Agreement
11// For Open Source Computer Vision Library
12//
13// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15// Third party copyrights are property of their respective owners.
16//
17// Redistribution and use in source and binary forms, with or without modification,
18// are permitted provided that the following conditions are met:
19//
20// * Redistribution's of source code must retain the above copyright notice,
21// this list of conditions and the following disclaimer.
22//
23// * Redistribution's in binary form must reproduce the above copyright notice,
24// this list of conditions and the following disclaimer in the documentation
25// and/or other materials provided with the distribution.
26//
27// * The name of the copyright holders may not be used to endorse or promote products
28// derived from this software without specific prior written permission.
29//
30// This software is provided by the copyright holders and contributors "as is" and
31// any express or implied warranties, including, but not limited to, the implied
32// warranties of merchantability and fitness for a particular purpose are disclaimed.
33// In no event shall the Intel Corporation or contributors be liable for any direct,
34// indirect, incidental, special, exemplary, or consequential damages
35// (including, but not limited to, procurement of substitute goods or services;
36// loss of use, data, or profits; or business interruption) however caused
37// and on any theory of liability, whether in contract, strict liability,
38// or tort (including negligence or otherwise) arising in any way out of
39// the use of this software, even if advised of the possibility of such damage.
40//
41//M*/
42
43#include "precomp.hpp"
44#include "utils.hpp"
45#include "grfmt_pxm.hpp"
46#include <opencv2/core/utils/logger.hpp>
47
48#ifdef HAVE_IMGCODEC_PXM
49
50namespace cv
51{
52
53///////////////////////// P?M reader //////////////////////////////
54
55static int ReadNumber(RLByteStream& strm, int maxdigits = 0)
56{
57 int code;
58 int64 val = 0;
59 int digits = 0;
60
61 code = strm.getByte();
62
63 while (!isdigit(code))
64 {
65 if (code == '#' )
66 {
67 do
68 {
69 code = strm.getByte();
70 }
71 while (code != '\n' && code != '\r');
72 code = strm.getByte();
73 }
74 else if (isspace(code))
75 {
76 while (isspace(code))
77 code = strm.getByte();
78 }
79 else
80 {
81#if 1
82 CV_Error_(Error::StsError, ("PXM: Unexpected code in ReadNumber(): 0x%x (%d)", code, code));
83#else
84 code = strm.getByte();
85#endif
86 }
87 }
88
89 do
90 {
91 val = val*10 + (code - '0');
92 CV_Assert(val <= INT_MAX && "PXM: ReadNumber(): result is too large");
93 digits++;
94 if (maxdigits != 0 && digits >= maxdigits) break;
95 code = strm.getByte();
96 }
97 while (isdigit(code));
98
99 return (int)val;
100}
101
102
103PxMDecoder::PxMDecoder()
104{
105 m_offset = -1;
106 m_buf_supported = true;
107 m_bpp = 0;
108 m_binary = false;
109 m_maxval = 0;
110}
111
112
113PxMDecoder::~PxMDecoder()
114{
115 close();
116}
117
118size_t PxMDecoder::signatureLength() const
119{
120 return 3;
121}
122
123bool PxMDecoder::checkSignature( const String& signature ) const
124{
125 return signature.size() >= 3 && signature[0] == 'P' &&
126 '1' <= signature[1] && signature[1] <= '6' &&
127 isspace(signature[2]);
128}
129
130ImageDecoder PxMDecoder::newDecoder() const
131{
132 return makePtr<PxMDecoder>();
133}
134
135void PxMDecoder::close()
136{
137 m_strm.close();
138}
139
140
141bool PxMDecoder::readHeader()
142{
143 bool result = false;
144
145 if( !m_buf.empty() )
146 {
147 if( !m_strm.open(buf: m_buf) )
148 return false;
149 }
150 else if( !m_strm.open( filename: m_filename ))
151 return false;
152
153 try
154 {
155 int code = m_strm.getByte();
156 if( code != 'P' )
157 throw RBS_BAD_HEADER;
158
159 code = m_strm.getByte();
160 switch( code )
161 {
162 case '1': case '4': m_bpp = 1; break;
163 case '2': case '5': m_bpp = 8; break;
164 case '3': case '6': m_bpp = 24; break;
165 default: throw RBS_BAD_HEADER;
166 }
167
168 m_binary = code >= '4';
169 m_type = m_bpp > 8 ? CV_8UC3 : CV_8UC1;
170
171 m_width = ReadNumber(strm&: m_strm);
172 m_height = ReadNumber(strm&: m_strm);
173
174 m_maxval = m_bpp == 1 ? 1 : ReadNumber(strm&: m_strm);
175 if( m_maxval > 65535 )
176 throw RBS_BAD_HEADER;
177
178 //if( m_maxval > 255 ) m_binary = false; nonsense
179 if( m_maxval > 255 )
180 m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
181
182 if( m_width > 0 && m_height > 0 && m_maxval > 0 && m_maxval < (1 << 16))
183 {
184 m_offset = m_strm.getPos();
185 result = true;
186 }
187 }
188 catch (const cv::Exception&)
189 {
190 throw;
191 }
192 catch (...)
193 {
194 CV_LOG_ERROR(NULL, "PXM::readHeader(): unknown C++ exception");
195 throw;
196 }
197
198 if( !result )
199 {
200 m_offset = -1;
201 m_width = m_height = -1;
202 m_strm.close();
203 }
204 return result;
205}
206
207
208bool PxMDecoder::readData( Mat& img )
209{
210 bool color = img.channels() > 1;
211 uchar* data = img.ptr();
212 PaletteEntry palette[256];
213 bool result = false;
214 const int bit_depth = CV_ELEM_SIZE1(m_type)*8;
215 const int src_pitch = divUp(a: m_width*m_bpp*(bit_depth/8), b: 8);
216 int nch = CV_MAT_CN(m_type);
217 int width3 = m_width*nch;
218
219 if( m_offset < 0 || !m_strm.isOpened())
220 return false;
221
222 uchar gray_palette[256] = {0};
223
224 // create LUT for converting colors
225 if( bit_depth == 8 )
226 {
227 CV_Assert(m_maxval < 256 && m_maxval > 0);
228
229 for (int i = 0; i <= m_maxval; i++)
230 gray_palette[i] = (uchar)((i*255/m_maxval)^(m_bpp == 1 ? 255 : 0));
231
232 FillGrayPalette( palette, bpp: m_bpp==1 ? 1 : 8 , negative: m_bpp == 1 );
233 }
234
235 try
236 {
237 m_strm.setPos( m_offset );
238
239 switch( m_bpp )
240 {
241 ////////////////////////// 1 BPP /////////////////////////
242 case 1:
243 CV_Assert(CV_MAT_DEPTH(m_type) == CV_8U);
244 if( !m_binary )
245 {
246 AutoBuffer<uchar> _src(m_width);
247 uchar* src = _src.data();
248
249 for (int y = 0; y < m_height; y++, data += img.step)
250 {
251 for (int x = 0; x < m_width; x++)
252 src[x] = ReadNumber(strm&: m_strm, maxdigits: 1) != 0;
253
254 if( color )
255 FillColorRow8( data, indices: src, len: m_width, palette );
256 else
257 FillGrayRow8( data, indices: src, len: m_width, palette: gray_palette );
258 }
259 }
260 else
261 {
262 AutoBuffer<uchar> _src(src_pitch);
263 uchar* src = _src.data();
264
265 for (int y = 0; y < m_height; y++, data += img.step)
266 {
267 m_strm.getBytes( buffer: src, count: src_pitch );
268
269 if( color )
270 FillColorRow1( data, indices: src, len: m_width, palette );
271 else
272 FillGrayRow1( data, indices: src, len: m_width, palette: gray_palette );
273 }
274 }
275 result = true;
276 break;
277
278 ////////////////////////// 8 BPP /////////////////////////
279 case 8:
280 case 24:
281 {
282 AutoBuffer<uchar> _src(std::max<size_t>(a: width3*2, b: src_pitch));
283 uchar* src = _src.data();
284
285 for (int y = 0; y < m_height; y++, data += img.step)
286 {
287 if( !m_binary )
288 {
289 for (int x = 0; x < width3; x++)
290 {
291 int code = ReadNumber(strm&: m_strm);
292 if( (unsigned)code > (unsigned)m_maxval ) code = m_maxval;
293 if( bit_depth == 8 )
294 src[x] = gray_palette[code];
295 else
296 ((ushort *)src)[x] = (ushort)code;
297 }
298 }
299 else
300 {
301 m_strm.getBytes( buffer: src, count: src_pitch );
302 if( bit_depth == 16 && !isBigEndian() )
303 {
304 for (int x = 0; x < width3; x++)
305 {
306 uchar v = src[x * 2];
307 src[x * 2] = src[x * 2 + 1];
308 src[x * 2 + 1] = v;
309 }
310 }
311 }
312
313 if( img.depth() == CV_8U && bit_depth == 16 )
314 {
315 for (int x = 0; x < width3; x++)
316 {
317 int v = ((ushort *)src)[x];
318 src[x] = (uchar)(v >> 8);
319 }
320 }
321
322 if( m_bpp == 8 ) // image has one channel
323 {
324 if( color )
325 {
326 if( img.depth() == CV_8U ) {
327 uchar *d = data, *s = src, *end = src + m_width;
328 for( ; s < end; d += 3, s++)
329 d[0] = d[1] = d[2] = *s;
330 } else {
331 ushort *d = (ushort *)data, *s = (ushort *)src, *end = ((ushort *)src) + m_width;
332 for( ; s < end; s++, d += 3)
333 d[0] = d[1] = d[2] = *s;
334 }
335 }
336 else
337 memcpy(dest: data, src: src, n: img.elemSize1()*m_width);
338 }
339 else
340 {
341 if( color )
342 {
343 if (m_use_rgb)
344 memcpy(dest: data, src: src, n: m_width * CV_ELEM_SIZE(img.type()));
345 else if( img.depth() == CV_8U )
346 icvCvt_RGB2BGR_8u_C3R( bgr: src, bgr_step: 0, rgb: data, rgb_step: 0, size: Size(m_width,1) );
347 else
348 icvCvt_RGB2BGR_16u_C3R( bgr: (ushort *)src, bgr_step: 0, rgb: (ushort *)data, rgb_step: 0, size: Size(m_width,1) );
349 }
350 else if( img.depth() == CV_8U )
351 icvCvt_BGR2Gray_8u_C3C1R( bgr: src, bgr_step: 0, gray: data, gray_step: 0, size: Size(m_width,1), swap_rb: 2 );
352 else
353 icvCvt_BGRA2Gray_16u_CnC1R( bgra: (ushort *)src, bgra_step: 0, gray: (ushort *)data, gray_step: 0, size: Size(m_width,1), ncn: 3, swap_rb: 2 );
354 }
355 }
356 result = true;
357 break;
358 }
359 default:
360 CV_Error(Error::StsError, "m_bpp is not supported");
361 }
362 }
363 catch (const cv::Exception&)
364 {
365 throw;
366 }
367 catch (...)
368 {
369 CV_LOG_ERROR(NULL, "PXM::readData(): unknown exception");
370 throw;
371 }
372
373 return result;
374}
375
376
377//////////////////////////////////////////////////////////////////////////////////////////
378
379PxMEncoder::PxMEncoder(PxMMode mode) :
380 mode_(mode)
381{
382 switch (mode)
383 {
384 case PXM_TYPE_AUTO: m_description = "Portable image format - auto (*.pnm)"; break;
385 case PXM_TYPE_PBM: m_description = "Portable image format - monochrome (*.pbm)"; break;
386 case PXM_TYPE_PGM: m_description = "Portable image format - gray (*.pgm)"; break;
387 case PXM_TYPE_PPM: m_description = "Portable image format - color (*.ppm)"; break;
388 default:
389 CV_Error(Error::StsInternal, "");
390 }
391 m_buf_supported = true;
392}
393
394PxMEncoder::~PxMEncoder()
395{
396}
397
398bool PxMEncoder::isFormatSupported(int depth) const
399{
400 if (mode_ == PXM_TYPE_PBM)
401 return depth == CV_8U;
402 return depth == CV_8U || depth == CV_16U;
403}
404
405bool PxMEncoder::write(const Mat& img, const std::vector<int>& params)
406{
407 bool isBinary = true;
408
409 int width = img.cols, height = img.rows;
410 int _channels = img.channels(), depth = (int)img.elemSize1()*8;
411 int channels = _channels > 1 ? 3 : 1;
412 int fileStep = width*(int)img.elemSize();
413 int x, y;
414
415 for( size_t i = 0; i < params.size(); i += 2 )
416 {
417 if( params[i] == IMWRITE_PXM_BINARY )
418 isBinary = params[i+1] != 0;
419 }
420
421 int mode = mode_;
422 if (mode == PXM_TYPE_AUTO)
423 {
424 mode = img.channels() == 1 ? PXM_TYPE_PGM : PXM_TYPE_PPM;
425 }
426
427 if (mode == PXM_TYPE_PGM && img.channels() > 1)
428 {
429 CV_Error(Error::StsBadArg, "Portable bitmap(.pgm) expects gray image");
430 }
431 if (mode == PXM_TYPE_PPM && img.channels() != 3)
432 {
433 CV_Error(Error::StsBadArg, "Portable bitmap(.ppm) expects BGR image");
434 }
435 if (mode == PXM_TYPE_PBM && img.type() != CV_8UC1)
436 {
437 CV_Error(Error::StsBadArg, "For portable bitmap(.pbm) type must be CV_8UC1");
438 }
439
440 WLByteStream strm;
441
442 if( m_buf )
443 {
444 if( !strm.open(buf&: *m_buf) )
445 return false;
446 int t = CV_MAKETYPE(img.depth(), channels);
447 m_buf->reserve( n: alignSize(sz: 256 + (isBinary ? fileStep*height :
448 ((t == CV_8UC1 ? 4 : t == CV_8UC3 ? 4*3+2 :
449 t == CV_16UC1 ? 6 : 6*3+2)*width+1)*height), n: 256));
450 }
451 else if( !strm.open(filename: m_filename) )
452 return false;
453
454 int lineLength;
455 int bufferSize = 128; // buffer that should fit a header
456
457 if( isBinary )
458 lineLength = width * (int)img.elemSize();
459 else
460 lineLength = (6 * channels + (channels > 1 ? 2 : 0)) * width + 32;
461
462 if( bufferSize < lineLength )
463 bufferSize = lineLength;
464
465 AutoBuffer<char> _buffer(bufferSize);
466 char* buffer = _buffer.data();
467
468 // write header;
469 const int code = ((mode == PXM_TYPE_PBM) ? 1 : (mode == PXM_TYPE_PGM) ? 2 : 3)
470 + (isBinary ? 3 : 0);
471
472 int header_sz = snprintf(s: buffer, maxlen: bufferSize, format: "P%c\n%d %d\n",
473 (char)('0' + code), width, height);
474 CV_Assert(header_sz > 0);
475 if (mode != PXM_TYPE_PBM)
476 {
477 int sz = snprintf(s: &buffer[header_sz], maxlen: bufferSize - header_sz, format: "%d\n", (1 << depth) - 1);
478 CV_Assert(sz > 0);
479 header_sz += sz;
480 }
481
482 CHECK_WRITE(strm.putBytes(buffer, header_sz));
483
484 for( y = 0; y < height; y++ )
485 {
486 const uchar* const data = img.ptr(y);
487 if( isBinary )
488 {
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 CHECK_WRITE(strm.putBytes(buffer, (int)(ptr - buffer)));
516 continue;
517 }
518
519 if( _channels == 3 )
520 {
521 if( depth == 8 )
522 icvCvt_BGR2RGB_8u_C3R( bgr: (const uchar*)data, bgr_step: 0,
523 rgb: (uchar*)buffer, rgb_step: 0, size: Size(width,1) );
524 else
525 icvCvt_BGR2RGB_16u_C3R( bgr: (const ushort*)data, bgr_step: 0,
526 rgb: (ushort*)buffer, rgb_step: 0, size: Size(width,1) );
527 }
528
529 // swap endianness if necessary
530 if( depth == 16 && !isBigEndian() )
531 {
532 if( _channels == 1 )
533 memcpy( dest: buffer, src: data, n: fileStep );
534 for( x = 0; x < width*channels*2; x += 2 )
535 {
536 uchar v = buffer[x];
537 buffer[x] = buffer[x + 1];
538 buffer[x + 1] = v;
539 }
540 }
541
542 CHECK_WRITE(strm.putBytes( (channels > 1 || depth > 8) ? buffer : (const char*)data, fileStep));
543 }
544 else
545 {
546 char* ptr = buffer;
547 if (mode == PXM_TYPE_PBM)
548 {
549 CV_Assert(channels == 1);
550 CV_Assert(depth == 8);
551 for (x = 0; x < width; x++)
552 {
553 ptr[0] = data[x] ? '0' : '1';
554 ptr += 1;
555 }
556 }
557 else
558 {
559 if( channels > 1 )
560 {
561 if( depth == 8 )
562 {
563 for( x = 0; x < width*channels; x += channels )
564 {
565 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 4d", data[x + 2] );
566 ptr += 4;
567 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 4d", data[x + 1] );
568 ptr += 4;
569 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 4d", data[x] );
570 ptr += 4;
571 *ptr++ = ' ';
572 *ptr++ = ' ';
573 }
574 }
575 else
576 {
577 for( x = 0; x < width*channels; x += channels )
578 {
579 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 6d", ((const ushort *)data)[x + 2] );
580 ptr += 6;
581 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 6d", ((const ushort *)data)[x + 1] );
582 ptr += 6;
583 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 6d", ((const ushort *)data)[x] );
584 ptr += 6;
585 *ptr++ = ' ';
586 *ptr++ = ' ';
587 }
588 }
589 }
590 else
591 {
592 if( depth == 8 )
593 {
594 for( x = 0; x < width; x++ )
595 {
596 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 4d", data[x] );
597 ptr += 4;
598 }
599 }
600 else
601 {
602 for( x = 0; x < width; x++ )
603 {
604 snprintf( s: ptr, maxlen: bufferSize - (ptr - buffer), format: "% 6d", ((const ushort *)data)[x] );
605 ptr += 6;
606 }
607 }
608 }
609 }
610
611 *ptr++ = '\n';
612
613 CHECK_WRITE(strm.putBytes( buffer, (int)(ptr - buffer) ));
614 }
615 }
616
617 strm.close();
618 return true;
619}
620
621}
622
623#endif // HAVE_IMGCODEC_PXM
624

source code of opencv/modules/imgcodecs/src/grfmt_pxm.cpp