1 /*
2 * PGF: A PGF-codec demonstration
3 * $Date: 2006-05-09 20:13:33 +0200 (Di, 09 Mai 2006) $
4 * $Revision: 187 $
5
6 * This file Copyright (C) 2006 xeraina GmbH, Switzerland
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23 // PGF.cpp : Defines the entry point for the console application.
24 //
25 /////////////////////////////////////////////////////////////////////////////
26 // definitions
27 #define PGFConsoleVersion "6.15.32" // Major number, Minor number: Year (2) Week (2)
28 #define CurrentYear "2015"
29 #define __PGFROISUPPORT__ // enables ROI support
30
31 #include <iostream>
32 #include <cmath> // or #include <math.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <ctime>
36 #include <fcntl.h>
37 #include <string>
38
39 #if defined(__linux__) || defined(__APPLE__)
40 #define __POSIX__
41 #endif
42
43 #ifdef __POSIX__
44 #include <libpgf/PGFimage.h>
45 #include <unistd.h> // open, close
46 #include <stdio.h>
47 #include <errno.h>
48 #include <string.h>
49 #else
50 #include "PGFimage.h"
51 #include <windows.h>
52 #endif
53
54 // must be included after PGFimage.h to avoid type conflicts on POSIX systems
55 #include "CImage.h"
56
57 /////////////////////////////////////////////////////////////////////////////
58 // definitions
59 #ifdef __APPLE__
60 #define __stat64 stat
61 #define _stat64 stat
62 #elif defined __POSIX__
63 #define __stat64 stat64
64 #define _stat64 stat64
65 #endif
66
67
68 using namespace std;
69
70 /////////////////////////////////////////////////////////////////////////////
71 // static variables
72 static bool bQuiet = false;
73 static string PGFErrors[] = {
74 "no error",
75 "memory allocation was not successfull",
76 "invalid memory stream position",
77 "user break by ESC",
78 "wrong pgf version",
79 "wrong data file format",
80 "image is too small",
81 "error in zlib functions",
82 "errors related to color table size",
83 "errors in png functions",
84 "expected data cannot be read",
85 };
86
87 /////////////////////////////////////////////////////////////////////////////
FileSize(char * filename)88 static INT64 FileSize(char *filename) {
89 struct __stat64 data;
90
91 if (_stat64(filename, &data) != -1) {
92 return data.st_size;
93 } else {
94 return 0;
95 }
96 }
97
98 /////////////////////////////////////////////////////////////////////////////
PSNR(const CImage & image1,const CImage & image2,bool roi,PGFRect & rect)99 static void PSNR(const CImage& image1, const CImage& image2, bool roi, PGFRect& rect) {
100 ASSERT(image1.GetChannelDepth() == image2.GetChannelDepth());
101 ASSERT(image1.GetChannelDepth() == 8 || image1.GetChannelDepth() == 16);
102
103 const int channels1 = image1.GetChannels(); ASSERT(channels1 <= MaxChannels);
104 #pragma warning(disable: 4189)
105 const int channels2 = image2.GetChannels(); ASSERT(channels2 <= MaxChannels);
106 ASSERT(channels1 == channels2);
107 const UINT32 w = image2.GetWidth();
108 const UINT32 h = image2.GetHeight();
109 const UINT32 size = w*h;
110 const int bypp1 = image1.GetBPP()/8; // RGB mode has 3 channels but can use 24 or 32 bits per pixel
111 const int bypp2 = image2.GetBPP()/8;
112
113 int pitch1 = image1.GetPitch();
114 int pitch2 = image2.GetPitch();
115 double sum[MaxChannels + 1] = { 0 };
116 int tmp;
117 int cnt1 = 0, cnt2 = 0, maxValue;
118
119 if (image1.GetChannelDepth() == 8) {
120 UINT8* rgbBuff1 = (UINT8 *)image1.GetBits();
121 UINT8* rgbBuff2 = (UINT8 *)image2.GetBits();
122 maxValue = 255;
123
124 #ifdef __PGFROISUPPORT__
125 if (roi) {
126 ASSERT(w <= image1.GetWidth());
127 ASSERT(h <= image1.GetHeight());
128 rgbBuff1 += (image1.GetHeight() - h - rect.top)*pitch1 + rect.left*channels1;
129 }
130 #endif
131 for (UINT32 j=0; j < h; j++) {
132 cnt1 = cnt2 = 0;
133 for (UINT32 i=0; i < w; i++) {
134 for (int c=0; c < channels1; c++) {
135 tmp = rgbBuff2[cnt2 + c] - rgbBuff1[cnt1 + c]; sum[c] += tmp*tmp;
136 }
137 cnt1 += bypp1;
138 cnt2 += bypp2;
139 }
140 rgbBuff1 += pitch1;
141 rgbBuff2 += pitch2;
142 }
143 } else if (image1.GetChannelDepth() == 16) {
144 UINT16* rgbBuff1 = (UINT16 *)image1.GetBits();
145 UINT16* rgbBuff2 = (UINT16 *)image2.GetBits();
146
147 #ifdef __PNMEXSUPPORT__
148 maxValue = image1.GetMaxValue();
149 #else
150 maxValue = 65535;
151 #endif
152 pitch1 /= 2;
153 pitch2 /= 2;
154
155 #ifdef __PGFROISUPPORT__
156 if (roi) {
157 ASSERT(w <= image1.GetWidth());
158 ASSERT(h <= image1.GetHeight());
159 rgbBuff1 += (image1.GetHeight() - h - rect.top)*pitch1 + rect.left*channels1;
160 }
161 #endif
162 for (UINT32 j=0; j < h; j++) {
163 cnt1 = cnt2 = 0;
164 for (UINT32 i=0; i < w; i++) {
165 for (int c=0; c < channels1; c++) {
166 tmp = rgbBuff2[cnt2 + c] - rgbBuff1[cnt1 + c]; sum[c] += tmp*tmp;
167 }
168 cnt1 += bypp1;
169 cnt2 += bypp2;
170 }
171 rgbBuff1 += pitch1;
172 rgbBuff2 += pitch2;
173 }
174 } else {
175 return;
176 }
177
178 for (int c=0; c < channels1; c++) {
179 sum[MaxChannels] += sum[c];
180 }
181
182 // output
183 if (bQuiet) {
184 cout << 10*log10(double(maxValue)*maxValue*size*channels1/sum[MaxChannels]);
185 // cout << ((sum[MaxChannels] != 0) ? 10*log10(double(maxValue)*maxValue*size*channels1/sum[MaxChannels]) : 100);
186 for (int c=0; c < channels1; c++) {
187 cout << '\t' << 10*log10(double(maxValue)*maxValue*size/sum[c]);
188 //cout << '\t' << ((sum[c] != 0) ? 10*log10(double(maxValue)*maxValue*size/sum[c]) : 100);
189 }
190 cout << '\t';
191 } else {
192 if (sum[MaxChannels] == 0) {
193 cout << "PSNR: lossless" << endl;
194 } else {
195 cout << "PSNR: " << 10*log10(double(maxValue)*maxValue*size*channels1/sum[MaxChannels]) << " (";
196 for (int c=0; c < channels1; c++) {
197 cout << 'c' << c << ": " << 10*log10(double(maxValue)*maxValue*size/sum[c]);
198 //cout << 'c' << c << ": " << ((sum[c] != 0) ? 10*log10(double(maxValue)*maxValue*size/sum[c]) : 100);
199 if (c < channels1 - 1) cout << ", ";
200 }
201 cout << ')' << endl;
202 }
203 cout << endl;
204 }
205 }
206
207 /////////////////////////////////////////////////////////////////////////////
Encoding(CImage * & image,char * source,char * dest,int levels,int quality,bool roi,bool streaming,CPGFMemoryStream ** memStream)208 static bool Encoding(CImage*& image, char *source, char *dest, int levels, int quality, bool roi, bool streaming, CPGFMemoryStream** memStream) {
209 ASSERT(source);
210 ASSERT(0 <= quality && quality <= MaxQuality);
211 bool returnValue = true;
212 CPGFImage pgf;
213 PGFHeader header;
214 clock_t start = 0, mean = 0, end = 0;
215 UINT32 writtenBytes = 0;
216 CPGFStream *stream = nullptr;
217 UINT8 bpp = 0;
218 #ifdef WIN32
219 HANDLE fd = nullptr;
220 #elif defined(__POSIX__)
221 int fd = 0;
222 #endif
223
224 if (!memStream) {
225 ASSERT(dest);
226 #ifdef WIN32
227 // no other choice to get a "HANDLE"
228 fd = CreateFile(dest, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
229 if (fd == INVALID_HANDLE_VALUE) {
230 cerr << "Error: Could not open destination file." << endl;
231 fd = nullptr;
232 returnValue = false;
233 goto CleanUp;
234 }
235
236 // create stream object
237 stream = new CPGFFileStream(fd);
238
239 #elif defined(__POSIX__)
240 fd = open(dest, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
241 if (fd == -1) {
242 cout << "Error: Could not open destination file." << endl;
243 fd = 0;
244 returnValue = false;
245 goto CleanUp;
246 }
247
248 // create stream object
249 stream = new CPGFFileStream(fd);
250 #endif
251 }
252
253 if (memStream) {
254 if (!bQuiet) cout << "Encoding PGF image to memory stream (quality: " << quality << ", levels: " << levels << "): " << source << endl;
255 } else {
256 if (!bQuiet) cout << "Encoding PGF image (quality: " << quality << ", levels: " << levels << "): " << source << endl;
257 }
258 if (roi || streaming) {
259 if (memStream) {
260 cout << "PGF image will support ROI." << endl;
261 } else {
262 cout << dest << " will support ROI." << endl;
263 }
264 }
265
266 start = clock();
267
268 // create new image object
269 image = new CImage();
270
271 // load image
272 #ifdef WIN32
273 if (!image->Load(source)) {
274 LPTSTR lpMsgBuf;
275 FormatMessage(
276 FORMAT_MESSAGE_ALLOCATE_BUFFER |
277 FORMAT_MESSAGE_FROM_SYSTEM |
278 FORMAT_MESSAGE_IGNORE_INSERTS,
279 nullptr,
280 GetLastError(),
281 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
282 (LPTSTR)&lpMsgBuf,
283 0,
284 nullptr
285 );
286 cerr << "Error: " << lpMsgBuf << endl;
287 LocalFree(lpMsgBuf);
288 returnValue = false;
289 goto CleanUp;
290 }
291 #elif defined(__POSIX__)
292 if (!image->Load(source) ){
293 cerr << "Error: Could not load source file." << endl;
294 returnValue = false;
295 goto CleanUp;
296 }
297 #endif
298
299 bpp = image->GetBPP();
300 // minimum: 1-bit bitmap, maximum: 48-bit RGB (16-bit per channel)
301 if ((bpp < 1) || (bpp > 48) || (bpp == 4)) {
302 cerr << "Error: Unhandled image format." << endl;
303 returnValue = false;
304 goto CleanUp;
305 }
306
307 ASSERT((bpp >= 1) && (bpp <= 48) && (bpp != 4));
308
309 if (memStream) {
310 *memStream = new CPGFMemoryStream(abs(image->GetPitch())*image->GetHeight());
311 stream = *memStream;
312 }
313
314 mean = clock();
315
316 // optional PGF encoder configuration
317 pgf.ConfigureEncoder(true, false); // true: use openMP (if codec is compiled with openMP), false: favorSpeedOverSize
318
319 header.width = image->GetWidth();
320 header.height = image->GetHeight();
321 header.nLevels = (UINT8)levels;
322 header.quality = (UINT8)quality;
323 header.mode = image->GetColorType();
324 header.bpp = header.channels = 0; // depend on mode and will be set automatically
325 header.usedBitsPerChannel = 0; // depend on mode and bpp and will be set automatically
326
327 try {
328 if (bQuiet) {
329 pgf.SetHeader(header, (roi || streaming) ? PGFROI : 0);
330 } else {
331 char data[] = "This is a free text annotation.";
332 pgf.SetHeader(header, (roi || streaming) ? PGFROI : 0, (UINT8 *)data, sizeof(data));
333 }
334
335 #ifdef __PNMEXSUPPORT__
336 if (image->GetChannelDepth() > 8) {
337 // set maximum value
338 pgf.SetMaxValue(image->GetMaxValue());
339 }
340 #endif
341
342 // copy bits
343 UINT8* buff = (UINT8 *)image->GetBits();
344 if (pgf.Mode() == ImageModeRGB48) {
345 int map[] = { 2, 1, 0 };
346 pgf.ImportBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1)*image->GetPitch()]), bpp, map);
347 } else {
348 #ifdef __BIG_ENDIAN__
349 int map[] = { 2, 1, 0 };
350 pgf.ImportBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1)*image->GetPitch()]), bpp, map);
351 #else
352 pgf.ImportBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1)*image->GetPitch()]), bpp);
353 #endif
354 }
355
356 // update color table if image is indexed
357 if ((pgf.Mode() == ImageModeIndexedColor)) {
358 int colorTableSize = image->GetMaxColorTableEntries();
359 RGBQUAD* pColorTable = new RGBQUAD[colorTableSize];
360
361 image->GetColorTable(0, colorTableSize, pColorTable);
362 pgf.SetColorTable(0, colorTableSize, pColorTable);
363 delete[] pColorTable;
364 }
365
366 } catch(IOException& e) {
367 int err = e.error;
368
369 if (err >= AppError) {
370 cerr << "Error: Importing input image failed\n(" << PGFErrors[err - AppError] << ")!" << endl;
371 } else {
372 cerr << "Error: Importing input image failed (" << err << ")!" << endl;
373 }
374 returnValue = false;
375 goto CleanUp;
376 }
377
378 try {
379 if (streaming) {
380 // process and write header
381 writtenBytes = pgf.WriteHeader(stream);
382 if (!bQuiet) cout << "Write header [" << writtenBytes << " bytes]" << endl;
383 // encode each level separately
384 for(int i = pgf.Levels() - 1; i >= 0; i--) {
385 UINT32 size = pgf.Write(i);
386 if (!bQuiet) cout << "Write level " << i << " [" << size << " bytes]" << endl;
387 writtenBytes += size;
388 }
389 } else {
390 // write image to pgf-file
391 pgf.Write(stream, &writtenBytes);
392 }
393
394 } catch(IOException& e) {
395 int err = e.error;
396 if (err >= AppError) {
397 cerr << "Error: Writing PGF image failed\n(" << PGFErrors[err - AppError] << ")!" << endl;
398 } else {
399 cerr << "Error: Writing PGF image failed (" << err << ")!" << endl;
400 }
401 returnValue = false;
402 goto CleanUp;
403 }
404
405 end = clock();
406
407 CleanUp:
408 if (memStream) {
409 // reset stream position
410 pgf.ResetStreamPos(false);
411 } else {
412 // close file
413 #ifdef WIN32
414 if (fd) CloseHandle(fd);
415 #elif defined(__POSIX__)
416 if (fd) close(fd);
417 #endif
418 delete stream;
419 }
420
421 if (returnValue) {
422 // output: output file has to be closed before FileSize
423 double destSize = (memStream) ? writtenBytes : double(FileSize(dest));
424 double ratio = double(FileSize(source))/destSize;
425
426 if (bQuiet) {
427 cout << double(end - mean)/CLOCKS_PER_SEC << '\t' << double(end - start)/CLOCKS_PER_SEC << '\t' << ratio << '\t';
428 } else {
429 cout << "Written bytes: " << writtenBytes << endl;
430 cout << "Encoding time (encoding, writing PGF): " << double(end - mean)/CLOCKS_PER_SEC << " s" << endl;
431 cout << "Total time (reading source, encoding, writing PGF): " << double(end - start)/CLOCKS_PER_SEC << " s" << endl;
432 cout << "Compression ratio: " << ratio << endl;
433 cout << endl;
434 }
435 }
436
437 return returnValue;
438 }
439
440 /////////////////////////////////////////////////////////////////////////////
Decoding(CImage * & image,char * source,char * dest,bool roi,PGFRect & rect,bool streaming,CPGFMemoryStream ** memStream)441 static bool Decoding(CImage*& image, char *source, char *dest, bool roi, PGFRect& rect, bool streaming, CPGFMemoryStream** memStream) {
442 ASSERT(dest);
443 bool returnValue = true;
444 CPGFImage pgf;
445 clock_t start = 0, mean = 0, mean2 = 0, mean3 = 0, end = 0;
446 CPGFStream *stream = nullptr;
447 UINT8* buff = nullptr;
448 double sourceSize = 0;
449 #ifdef WIN32
450 HANDLE fd = nullptr;
451 #elif defined(__POSIX__)
452 int fd = 0;
453 #endif
454
455 if (memStream) {
456 // use memory stream
457 stream = *memStream;
458 } else {
459 ASSERT(source);
460 #ifdef WIN32
461 // no other choice to get a "HANDLE"
462 fd = CreateFile(source, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
463 if (fd == INVALID_HANDLE_VALUE) {
464 cerr << "Error: Could not open source file." << endl;
465 fd = nullptr;
466 returnValue = false;
467 goto CleanUp;
468 }
469
470 // create stream object
471 stream = new CPGFFileStream(fd);
472
473 #elif defined(__POSIX__)
474 fd = open(source, O_RDONLY);
475 if (fd == -1){
476 cout << "Error: Could not open source file." << endl;
477 fd = 0;
478 returnValue = false;
479 goto CleanUp;
480 }
481
482 // create stream object
483 stream = new CPGFFileStream(fd);
484 #endif
485 }
486
487 // optional PGF encoder configuration
488 pgf.ConfigureDecoder(true); // true: use openMP (if codec is compiled with openMP)
489
490 if (memStream) {
491 if (!bQuiet) cout << "Decoding PGF image from memory stream." << endl;
492 } else {
493 if (!bQuiet) cout << "Decoding PGF image: " << source << endl;
494 }
495
496 start = clock();
497 try {
498 // open pgf image
499 UINT64 startpos = stream->GetPos();
500 pgf.Open(stream);
501 if (!bQuiet) cout << "Read header and level info [" << stream->GetPos() - startpos << " bytes]" << endl;
502
503 // read annotations
504 if (!bQuiet) {
505 UINT32 len = 0;
506 const UINT8 *data = pgf.GetUserData(len);
507 if (data && len) {
508 cout << (char *)data << endl;
509 }
510 }
511
512 // read pgf image
513 #ifdef __PGFROISUPPORT__
514 if (pgf.ROIisSupported()) {
515 if (memStream) {
516 if (!bQuiet) cout << "PGF image supports ROI." << endl;
517 } else {
518 if (!bQuiet) cout << source << " supports ROI." << endl;
519 }
520 }
521 if (roi) {
522 pgf.Read(rect); // ROI test
523 // example of how to make a second read (with a different ROI) after resetting stream position to data
524 //pgf.ResetStreamPos(true);
525 //pgf.Read(rect);
526 } else
527 #else
528 cout << "PGF Console: ROI supports has been disabled." << endl;
529 #endif
530 {
531 if (streaming) {
532 // decode each level separately
533 for(int i = pgf.Levels() - 1; i >= 0; i--) {
534 if (!bQuiet) cout << "Read level " << i;
535 UINT64 pos = stream->GetPos();
536 pgf.Read(i);
537 if (!bQuiet) cout << " [" << stream->GetPos() - pos << " bytes]" << endl;
538 }
539 } else {
540 // read entire file down to level 0
541 pgf.Read();
542 }
543 }
544 sourceSize = double(stream->GetPos() - startpos);
545
546 } catch(IOException& e) {
547 int err = e.error;
548 if (err >= AppError) {
549 cerr << "Error: Opening and reading PGF image failed\n(" << PGFErrors[err - AppError] << ")!" << endl;
550 } else {
551 cerr << "Error: Opening and reading PGF image failed (" << err << ")!" << endl;
552 }
553
554 returnValue = false;
555 goto CleanUp;
556 }
557 mean = clock();
558
559 // create image
560 image = new CImage();
561 #ifdef __PGFROISUPPORT__
562 if (roi) {
563 if (!image->Create(rect.Width(), rect.Height(), pgf.GetHeader()->mode)) {
564 cerr << "Decoding is not supported for this PGF color format." << endl;
565 returnValue = false;
566 goto CleanUp;
567 }
568 } else
569 #endif
570 {
571 if (!image->Create(pgf.Width(), pgf.Height(), pgf.GetHeader()->mode)) {
572 cerr << "Decoding is not supported for this PGF color format." << endl;
573 returnValue = false;
574 goto CleanUp;
575 }
576 }
577
578 // copy bits
579 mean2 = clock();
580 buff = (UINT8 *)image->GetBits();
581 if (pgf.Mode() == ImageModeRGB48) {
582 int map[] = { 2, 1, 0 };
583 pgf.GetBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1) * image->GetPitch()]), image->GetBPP(), map);
584 } else {
585 #ifdef __BIG_ENDIAN__
586 int map[] = { 2, 1, 0 };
587 pgf.GetBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1) * image->GetPitch()]), image->GetBPP(), map);
588 #else
589 pgf.GetBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1) * image->GetPitch()]), image->GetBPP());
590 #endif
591 }
592
593 // update color table if image is indexed or bitmap
594 if ((pgf.Mode() == ImageModeIndexedColor)) {
595 // cannot get number of color table entries directly, so use 2^bitdepth
596 image->SetColorTable(0, 1 << pgf.BPP(), pgf.GetColorTable());
597 } else if (pgf.GetHeader()->mode == ImageModeBitmap) {
598 RGBQUAD bw[2];
599 bw[0].rgbRed = 255;
600 bw[0].rgbGreen = 255;
601 bw[0].rgbBlue = 255;
602 bw[1].rgbRed = 0;
603 bw[1].rgbGreen = 0;
604 bw[1].rgbBlue = 0;
605 image->SetColorTable(0, 2, bw);
606 }
607
608 mean3 = clock();
609
610 #ifdef __PNMEXSUPPORT__
611 if (image->GetChannelDepth() > 8) {
612 // set maximum value
613 image->SetMaxValue(pgf.GetMaxValue());
614 }
615 #endif
616
617 // save image
618 if (image->Save(dest)) {
619 if (!bQuiet) cout << "Written image file: " << dest << endl;
620 } else {
621 cerr << "Error: Output format not supported for this color depth." << endl;
622 return false;
623 }
624
625 end = clock();
626
627 CleanUp:
628 if (memStream) {
629 stream->SetPos(FSFromStart, 0);
630 } else {
631 // close file
632 #ifdef WIN32
633 if (fd) CloseHandle(fd);
634 #elif defined(__POSIX__)
635 if (fd) close(fd);
636 #endif
637 delete stream;
638 }
639
640 if (!memStream) {
641 // source has to be closed before FileSize
642 sourceSize = (double)FileSize(source);
643 }
644
645 if (returnValue) {
646 // output
647 double ratio = double(FileSize(dest))/sourceSize;
648 if (bQuiet) {
649 cout << double(mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << '\t' << double(end - mean3 + mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << '\t' << ratio << '\t';
650 } else {
651 cout << "Decoding time (reading PGF, decoding): " << double(mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << " s" << endl;
652 cout << "Total time (reading PGF, decoding, writing destination): " << double(end - mean3 + mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << " s" << endl;
653 cout << "Compression ratio: " << ratio << endl;
654 cout << endl;
655 }
656 }
657 return returnValue;
658 }
659
660 /////////////////////////////////////////////////////////////////////////////
Measurement(char * source,char * dest,char * temp,int levels,int quality,bool roi,PGFRect & rect,bool streaming,bool useMemStream)661 static bool Measurement(char *source, char *dest, char *temp, int levels, int quality, bool roi, PGFRect& rect, bool streaming, bool useMemStream) {
662 ASSERT(source);
663 ASSERT(dest);
664 CImage *image1 = nullptr, *image2 = nullptr;
665 CPGFMemoryStream *memStream = nullptr;
666
667 if (!bQuiet) cout << "Measuring PSNR (quality: " << quality << ", levels: " << levels << "): " << source << endl;
668 if (useMemStream) {
669 if (!bQuiet) cout << "PGF image is written into memory stream." << endl;
670 } else {
671 ASSERT(temp);
672 }
673 if (!bQuiet) cout << endl;
674
675 if (!Encoding(image1, source, temp, levels, quality, roi, streaming, (useMemStream) ? &memStream : nullptr)) {
676 delete image1;
677 delete memStream;
678 return false;
679 }
680 if (!Decoding(image2, temp, dest, roi, rect, streaming, (useMemStream) ? &memStream : nullptr)) {
681 delete image2;
682 delete memStream;
683 return false;
684 }
685
686 if (image1->GetChannelDepth() == 8 || image1->GetChannelDepth() == 16) {
687 // output psnr
688 PSNR(*image1, *image2, roi, rect);
689 }
690
691 delete image1;
692 delete image2;
693 delete memStream;
694 return true;
695 }
696
697 /////////////////////////////////////////////////////////////////////////////
main(int argc,char * argv[])698 int main(int argc, char* argv[]) {
699 int nRetCode = 0;
700
701 // parse arguments
702 enum Operation {Encode, Decode, Measure} op = Encode;
703 char *source=nullptr, *dest=nullptr, *temp=nullptr;
704 int levels = 0, quality = 0;
705 PGFRect rect;
706 int arg = 1; // argument 0 = pgf
707 bool bSource = true; // true: bSource, false: dest
708 bool bStreaming = false;// true: level wise writing/reading is used
709 bool bROI = false; // true: ROI is used
710 bool bMemStream = false;// true: use a memory stream during measuring
711 bool bWrongArgs = (argc < 4);
712
713 while (!bWrongArgs && arg < argc) {
714 if (argv[arg][0] == '-') {
715 // options
716 switch(argv[arg][1]) {
717 case 'e': op = Encode; arg++; break;
718 case 'd': op = Decode; arg++; break;
719 case 'm': op = Measure;
720 if (argv[arg][2] == 'm') {
721 // -mm
722 bMemStream = true;
723 arg++;
724 } else {
725 arg++;
726 if (arg == argc) {
727 bWrongArgs = true;
728 } else {
729 temp = argv[arg]; arg++;
730 }
731 }
732 break;
733 case 'l': arg++;
734 if (arg == argc) {
735 bWrongArgs = true;
736 } else {
737 levels = atoi(argv[arg]); arg++;
738 bWrongArgs = (levels < 1) || (levels > MaxLevel);
739 }
740 break;
741 case 'q': arg++;
742 if (arg == argc) {
743 bWrongArgs = true;
744 } else {
745 quality = atoi(argv[arg]); arg++;
746 bWrongArgs = (quality < 0) || (quality > MaxQuality);
747 }
748 break;
749 case 'r': bROI = true;
750 if (argv[arg][2] == 'e') {
751 // -re[ct]
752 arg++;
753 if (arg + 4 > argc) {
754 bWrongArgs = true;
755 } else {
756 rect = PGFRect(atoi(argv[arg]), atoi(argv[arg+1]), atoi(argv[arg+2]), atoi(argv[arg+3]));
757 arg += 4;
758 }
759 } else {
760 // -r
761 arg++;
762 if (arg == argc) bWrongArgs = true;
763 }
764 break;
765 case 's': bStreaming = true; arg++; break;
766 case 'v': bQuiet = true; arg++; break;
767 default: arg++; bWrongArgs = true; break;
768 }
769 } else {
770 if (bSource) {
771 source = argv[arg];
772 bSource = false;
773 } else {
774 dest = argv[arg];
775 }
776 arg++;
777 }
778 }
779 if (!bQuiet) cout << "PGF Console - Copyright (c) 2001-" CurrentYear " xeraina GmbH, Switzerland" << endl <<
780 "Console Version: " PGFConsoleVersion ", www.xeraina.ch" << endl <<
781 "libpgf Version : " PGFCodecVersion ", www.libpgf.org" << endl << endl;
782 if (bWrongArgs) {
783 if (!bQuiet) {
784 cout << "Usage: " << endl <<
785 "- Encoding: pgfconsole -e [-l levels] [-q quality] [-r] [-s] [-v] source dest" << endl <<
786 " Create from a source file a PGF image (dest)." << endl <<
787 " The most popular image file formats with the following image" << endl <<
788 " types are supported:" << endl <<
789 " - bitmap (1 bit)" << endl <<
790 " - grayscale (8 and 16 bit)" << endl <<
791 " - indexed color (8 bit)" << endl <<
792 " - RGB (16 [565], 24, 32, and 48 bit)" << endl <<
793 " - RGBA (32 bit)" << endl <<
794 " Options:" << endl <<
795 " -l levels Number of hierarchical levels [1.." << MaxLevel << "]. Default is 0." << endl <<
796 " 0 means the number of levels are automatically set." << endl <<
797 " -q quality Quality [0.." << MaxQuality << "]. Default is 0." << endl <<
798 " 0 means perfect quality (lossless compression)," << endl <<
799 " " << MaxQuality << " means maximum compression." << endl <<
800 " -r Region of interest (ROI) encoding scheme is used." << endl <<
801 " This encoding scheme has a slightly worse compression ratio." << endl <<
802 " -s Level wise encoding in separate writing calls." << endl <<
803 " -v Numbers only: All text output is reduced to numbers." << endl <<
804 endl <<
805 "- Decoding: pgfconsole -d [-rect left top width height] [-s] [-v] source dest" << endl <<
806 " Create from a PGF image (source) a new image (dest)." << endl <<
807 " Options:" << endl <<
808 " -rect rect Read a rectangular region of a PGF image supporting Region of" << endl <<
809 " interests (ROI). The rectangle is defined by 4 blank-separated" << endl <<
810 " positive parameters: left top width height" << endl <<
811 " -s Level wise decoding in separate reading calls." << endl <<
812 " -v Numbers only: All text output is reduced to numbers." << endl <<
813 endl <<
814 "- Measuring: pgfconsole -m temp-file [...] source destination" << endl <<
815 " Measure quality between source and destination bitmap." << endl <<
816 " Encode from an input image (source) a PGF image" << endl <<
817 " (temp-file) and decode from the temp-file a new output" << endl <<
818 " (destination image)." << endl <<
819 " Options:" << endl <<
820 " -mm Instead of using the option -m temp-file you can use " << endl <<
821 " the option -mm (without temp-file). The latter writes the PGF" << endl <<
822 " image into a memory stream instead of a file stream." << endl <<
823 " In both cases all encoding and decoding options are valid." << endl <<
824 endl <<
825 endl;
826 }
827 nRetCode = 2;
828 } else {
829 CImage *image = 0;
830
831 #ifdef __PNMEXSUPPORT__
832 // register new PNM plugin
833 CImage::RegisterPNM();
834 #endif
835
836 switch(op) {
837 case Encode:
838 if (!Encoding(image, source, dest, levels, quality, bROI, bStreaming, nullptr)) nRetCode = 3;
839 break;
840 case Decode:
841 if (!Decoding(image, source, dest, bROI, rect, bStreaming, nullptr)) nRetCode = 4;
842 break;
843 case Measure:
844 if (!Measurement(source, dest, temp, levels, quality, bROI, rect, bStreaming, bMemStream)) nRetCode = 5;
845 break;
846 default:
847 nRetCode = 6;
848 ASSERT(false); // unhandled operation
849 }
850 if (bQuiet) cout << endl;
851
852 delete image;
853 }
854 return nRetCode;
855 }
856
857