Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgcodecs/src/grfmt_pxm.cpp
16337 views
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 <iostream>
47
48
#ifdef HAVE_IMGCODEC_PXM
49
50
namespace cv
51
{
52
53
///////////////////////// P?M reader //////////////////////////////
54
55
static 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
103
PxMDecoder::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
113
PxMDecoder::~PxMDecoder()
114
{
115
close();
116
}
117
118
size_t PxMDecoder::signatureLength() const
119
{
120
return 3;
121
}
122
123
bool 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
130
ImageDecoder PxMDecoder::newDecoder() const
131
{
132
return makePtr<PxMDecoder>();
133
}
134
135
void PxMDecoder::close()
136
{
137
m_strm.close();
138
}
139
140
141
bool PxMDecoder::readHeader()
142
{
143
bool result = false;
144
145
if( !m_buf.empty() )
146
{
147
if( !m_strm.open(m_buf) )
148
return false;
149
}
150
else if( !m_strm.open( m_filename ))
151
return false;
152
153
CV_TRY
154
{
155
int code = m_strm.getByte();
156
if( code != 'P' )
157
CV_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: CV_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(m_strm);
172
m_height = ReadNumber(m_strm);
173
174
m_maxval = m_bpp == 1 ? 1 : ReadNumber(m_strm);
175
if( m_maxval > 65535 )
176
CV_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
CV_CATCH (cv::Exception, e)
189
{
190
CV_UNUSED(e);
191
CV_RETHROW();
192
}
193
CV_CATCH_ALL
194
{
195
std::cerr << "PXM::readHeader(): unknown C++ exception" << std::endl << std::flush;
196
CV_RETHROW();
197
}
198
199
if( !result )
200
{
201
m_offset = -1;
202
m_width = m_height = -1;
203
m_strm.close();
204
}
205
return result;
206
}
207
208
209
bool PxMDecoder::readData( Mat& img )
210
{
211
bool color = img.channels() > 1;
212
uchar* data = img.ptr();
213
PaletteEntry palette[256];
214
bool result = false;
215
const int bit_depth = CV_ELEM_SIZE1(m_type)*8;
216
const int src_pitch = divUp(m_width*m_bpp*(bit_depth/8), 8);
217
int nch = CV_MAT_CN(m_type);
218
int width3 = m_width*nch;
219
220
if( m_offset < 0 || !m_strm.isOpened())
221
return false;
222
223
uchar gray_palette[256] = {0};
224
225
// create LUT for converting colors
226
if( bit_depth == 8 )
227
{
228
CV_Assert(m_maxval < 256 && m_maxval > 0);
229
230
for (int i = 0; i <= m_maxval; i++)
231
gray_palette[i] = (uchar)((i*255/m_maxval)^(m_bpp == 1 ? 255 : 0));
232
233
FillGrayPalette( palette, m_bpp==1 ? 1 : 8 , m_bpp == 1 );
234
}
235
236
CV_TRY
237
{
238
m_strm.setPos( m_offset );
239
240
switch( m_bpp )
241
{
242
////////////////////////// 1 BPP /////////////////////////
243
case 1:
244
CV_Assert(CV_MAT_DEPTH(m_type) == CV_8U);
245
if( !m_binary )
246
{
247
AutoBuffer<uchar> _src(m_width);
248
uchar* src = _src.data();
249
250
for (int y = 0; y < m_height; y++, data += img.step)
251
{
252
for (int x = 0; x < m_width; x++)
253
src[x] = ReadNumber(m_strm, 1) != 0;
254
255
if( color )
256
FillColorRow8( data, src, m_width, palette );
257
else
258
FillGrayRow8( data, src, m_width, gray_palette );
259
}
260
}
261
else
262
{
263
AutoBuffer<uchar> _src(src_pitch);
264
uchar* src = _src.data();
265
266
for (int y = 0; y < m_height; y++, data += img.step)
267
{
268
m_strm.getBytes( src, src_pitch );
269
270
if( color )
271
FillColorRow1( data, src, m_width, palette );
272
else
273
FillGrayRow1( data, src, m_width, gray_palette );
274
}
275
}
276
result = true;
277
break;
278
279
////////////////////////// 8 BPP /////////////////////////
280
case 8:
281
case 24:
282
{
283
AutoBuffer<uchar> _src(std::max<size_t>(width3*2, src_pitch));
284
uchar* src = _src.data();
285
286
for (int y = 0; y < m_height; y++, data += img.step)
287
{
288
if( !m_binary )
289
{
290
for (int x = 0; x < width3; x++)
291
{
292
int code = ReadNumber(m_strm);
293
if( (unsigned)code > (unsigned)m_maxval ) code = m_maxval;
294
if( bit_depth == 8 )
295
src[x] = gray_palette[code];
296
else
297
((ushort *)src)[x] = (ushort)code;
298
}
299
}
300
else
301
{
302
m_strm.getBytes( src, src_pitch );
303
if( bit_depth == 16 && !isBigEndian() )
304
{
305
for (int x = 0; x < width3; x++)
306
{
307
uchar v = src[x * 2];
308
src[x * 2] = src[x * 2 + 1];
309
src[x * 2 + 1] = v;
310
}
311
}
312
}
313
314
if( img.depth() == CV_8U && bit_depth == 16 )
315
{
316
for (int x = 0; x < width3; x++)
317
{
318
int v = ((ushort *)src)[x];
319
src[x] = (uchar)(v >> 8);
320
}
321
}
322
323
if( m_bpp == 8 ) // image has one channel
324
{
325
if( color )
326
{
327
if( img.depth() == CV_8U ) {
328
uchar *d = data, *s = src, *end = src + m_width;
329
for( ; s < end; d += 3, s++)
330
d[0] = d[1] = d[2] = *s;
331
} else {
332
ushort *d = (ushort *)data, *s = (ushort *)src, *end = ((ushort *)src) + m_width;
333
for( ; s < end; s++, d += 3)
334
d[0] = d[1] = d[2] = *s;
335
}
336
}
337
else
338
memcpy(data, src, img.elemSize1()*m_width);
339
}
340
else
341
{
342
if( color )
343
{
344
if( img.depth() == CV_8U )
345
icvCvt_RGB2BGR_8u_C3R( src, 0, data, 0, cvSize(m_width,1) );
346
else
347
icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)data, 0, cvSize(m_width,1) );
348
}
349
else if( img.depth() == CV_8U )
350
icvCvt_BGR2Gray_8u_C3C1R( src, 0, data, 0, cvSize(m_width,1), 2 );
351
else
352
icvCvt_BGRA2Gray_16u_CnC1R( (ushort *)src, 0, (ushort *)data, 0, cvSize(m_width,1), 3, 2 );
353
}
354
}
355
result = true;
356
break;
357
}
358
default:
359
CV_Error(Error::StsError, "m_bpp is not supported");
360
}
361
}
362
CV_CATCH (cv::Exception, e)
363
{
364
CV_UNUSED(e);
365
CV_RETHROW();
366
}
367
CV_CATCH_ALL
368
{
369
std::cerr << "PXM::readData(): unknown exception" << std::endl << std::flush;
370
CV_RETHROW();
371
}
372
373
return result;
374
}
375
376
377
//////////////////////////////////////////////////////////////////////////////////////////
378
379
PxMEncoder::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
394
PxMEncoder::~PxMEncoder()
395
{
396
}
397
398
bool 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
405
bool 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(*m_buf) )
445
return false;
446
int t = CV_MAKETYPE(img.depth(), channels);
447
m_buf->reserve( alignSize(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), 256));
450
}
451
else if( !strm.open(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
const char* comment = "# Generated by OpenCV " CV_VERSION "\n";
472
473
int header_sz = sprintf(buffer, "P%c\n%s%d %d\n",
474
(char)('0' + code), comment,
475
width, height);
476
CV_Assert(header_sz > 0);
477
if (mode != PXM_TYPE_PBM)
478
{
479
int sz = sprintf(&buffer[header_sz], "%d\n", (1 << depth) - 1);
480
CV_Assert(sz > 0);
481
header_sz += sz;
482
}
483
484
strm.putBytes(buffer, header_sz);
485
486
for( y = 0; y < height; y++ )
487
{
488
const uchar* const data = img.ptr(y);
489
if( isBinary )
490
{
491
if (mode == PXM_TYPE_PBM)
492
{
493
char* ptr = buffer;
494
int bcount = 7;
495
char byte = 0;
496
for (x = 0; x < width; x++)
497
{
498
if (bcount == 0)
499
{
500
if (data[x] == 0)
501
byte = (byte) | 1;
502
*ptr++ = byte;
503
bcount = 7;
504
byte = 0;
505
}
506
else
507
{
508
if (data[x] == 0)
509
byte = (byte) | (1 << bcount);
510
bcount--;
511
}
512
}
513
if (bcount != 7)
514
{
515
*ptr++ = byte;
516
}
517
strm.putBytes(buffer, (int)(ptr - buffer));
518
continue;
519
}
520
521
if( _channels == 3 )
522
{
523
if( depth == 8 )
524
icvCvt_BGR2RGB_8u_C3R( (const uchar*)data, 0,
525
(uchar*)buffer, 0, cvSize(width,1) );
526
else
527
icvCvt_BGR2RGB_16u_C3R( (const ushort*)data, 0,
528
(ushort*)buffer, 0, cvSize(width,1) );
529
}
530
531
// swap endianness if necessary
532
if( depth == 16 && !isBigEndian() )
533
{
534
if( _channels == 1 )
535
memcpy( buffer, data, fileStep );
536
for( x = 0; x < width*channels*2; x += 2 )
537
{
538
uchar v = buffer[x];
539
buffer[x] = buffer[x + 1];
540
buffer[x + 1] = v;
541
}
542
}
543
544
strm.putBytes( (channels > 1 || depth > 8) ? buffer : (const char*)data, fileStep);
545
}
546
else
547
{
548
char* ptr = buffer;
549
if (mode == PXM_TYPE_PBM)
550
{
551
CV_Assert(channels == 1);
552
CV_Assert(depth == 8);
553
for (x = 0; x < width; x++)
554
{
555
ptr[0] = data[x] ? '0' : '1';
556
ptr += 1;
557
}
558
}
559
else
560
{
561
if( channels > 1 )
562
{
563
if( depth == 8 )
564
{
565
for( x = 0; x < width*channels; x += channels )
566
{
567
sprintf( ptr, "% 4d", data[x + 2] );
568
ptr += 4;
569
sprintf( ptr, "% 4d", data[x + 1] );
570
ptr += 4;
571
sprintf( ptr, "% 4d", data[x] );
572
ptr += 4;
573
*ptr++ = ' ';
574
*ptr++ = ' ';
575
}
576
}
577
else
578
{
579
for( x = 0; x < width*channels; x += channels )
580
{
581
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 2] );
582
ptr += 6;
583
sprintf( ptr, "% 6d", ((const ushort *)data)[x + 1] );
584
ptr += 6;
585
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
586
ptr += 6;
587
*ptr++ = ' ';
588
*ptr++ = ' ';
589
}
590
}
591
}
592
else
593
{
594
if( depth == 8 )
595
{
596
for( x = 0; x < width; x++ )
597
{
598
sprintf( ptr, "% 4d", data[x] );
599
ptr += 4;
600
}
601
}
602
else
603
{
604
for( x = 0; x < width; x++ )
605
{
606
sprintf( ptr, "% 6d", ((const ushort *)data)[x] );
607
ptr += 6;
608
}
609
}
610
}
611
}
612
613
*ptr++ = '\n';
614
615
strm.putBytes( buffer, (int)(ptr - buffer) );
616
}
617
}
618
619
strm.close();
620
return true;
621
}
622
623
}
624
625
#endif // HAVE_IMGCODEC_PXM
626
627