1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Alekhin
2023-01-09 11:08:02 +00:00
880 changed files with 83958 additions and 9368 deletions
+8
View File
@@ -691,6 +691,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported");
}
}
if (params[i] == IMWRITE_EXR_DWA_COMPRESSION_LEVEL)
{
#if OPENEXR_VERSION_MAJOR >= 3
header.dwaCompressionLevel() = params[i + 1];
#else
CV_LOG_ONCE_WARNING(NULL, "Setting `IMWRITE_EXR_DWA_COMPRESSION_LEVEL` not supported in OpenEXR version " + std::to_string(OPENEXR_VERSION_MAJOR) + " (version 3 is required)");
#endif
}
}
if( channels == 3 || channels == 4 )
+16 -2
View File
@@ -141,14 +141,28 @@ bool HdrEncoder::write( const Mat& input_img, const std::vector<int>& params )
if(img.depth() != CV_32F) {
img.convertTo(img, CV_32FC3, 1/255.0f);
}
CV_Assert(params.empty() || params[0] == HDR_NONE || params[0] == HDR_RLE);
int compression = IMWRITE_HDR_COMPRESSION_RLE;
for (size_t i = 0; i + 1 < params.size(); i += 2)
{
switch (params[i])
{
case IMWRITE_HDR_COMPRESSION:
compression = params[i + 1];
break;
default:
break;
}
}
CV_Check(compression, compression == IMWRITE_HDR_COMPRESSION_NONE || compression == IMWRITE_HDR_COMPRESSION_RLE, "");
FILE *fout = fopen(m_filename.c_str(), "wb");
if(!fout) {
return false;
}
RGBE_WriteHeader(fout, img.cols, img.rows, NULL);
if(params.empty() || params[0] == HDR_RLE) {
if (compression == IMWRITE_HDR_COMPRESSION_RLE) {
RGBE_WritePixels_RLE(fout, const_cast<float*>(img.ptr<float>()), img.cols, img.rows);
} else {
RGBE_WritePixels(fout, const_cast<float*>(img.ptr<float>()), img.cols * img.rows);
-6
View File
@@ -50,12 +50,6 @@
namespace cv
{
enum HdrCompression
{
HDR_NONE = 0,
HDR_RLE = 1
};
// Radiance rgbe (.hdr) reader
class HdrDecoder CV_FINAL : public BaseImageDecoder
{
+754
View File
@@ -0,0 +1,754 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#ifdef HAVE_SPNG
/****************************************************************************************\
This part of the file implements PNG codec on base of libspng library,
in particular, this code is based on example.c from libspng
(see 3rdparty/libspng/LICENSE for copyright notice)
\****************************************************************************************/
#ifndef _LFS64_LARGEFILE
#define _LFS64_LARGEFILE 0
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 0
#endif
#include <spng.h>
#include <zlib.h>
#include "grfmt_spng.hpp"
/*
* libspng does not support RGB -> Gray conversion. In order to decode colorful images as grayscale
* we need conversion functions. In the previous png implementation(grfmt_png), the author was set
* to particular values for rgb coefficients. OpenCV icvCvt_BGR2Gray function values does not match
* with these values. (png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 );) For this codec implementation,
* slightly modified versions are implemented in the below of this page.
*/
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb);
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb);
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
ushort *gray, int gray_step,
cv::Size size, int ncn, int _swap_rb);
namespace cv
{
/////////////////////// SPngDecoder ///////////////////
SPngDecoder::SPngDecoder()
{
m_signature = "\x89\x50\x4e\x47\xd\xa\x1a\xa";
m_color_type = 0;
m_ctx = 0;
m_f = 0;
m_buf_supported = true;
m_buf_pos = 0;
m_bit_depth = 0;
}
SPngDecoder::~SPngDecoder()
{
close();
}
ImageDecoder SPngDecoder::newDecoder() const
{
return makePtr<SPngDecoder>();
}
void SPngDecoder::close()
{
if (m_f)
{
fclose(m_f);
m_f = 0;
}
if (m_ctx)
{
struct spng_ctx *ctx = (struct spng_ctx *)m_ctx;
spng_ctx_free(ctx);
m_ctx = 0;
}
}
int SPngDecoder::readDataFromBuf(void *sp_ctx, void *user, void *dst, size_t size)
{
/*
* typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length)
* Type definition for callback passed to spng_set_png_stream() for decoders.
* A read callback function should copy length bytes to dest and return 0 or SPNG_IO_EOF/SPNG_IO_ERROR on error.
*/
CV_UNUSED(sp_ctx);
SPngDecoder *decoder = (SPngDecoder *)(user);
CV_Assert(decoder);
const Mat &buf = decoder->m_buf;
if (decoder->m_buf_pos + size > buf.cols * buf.rows * buf.elemSize())
{
return SPNG_IO_ERROR;
}
memcpy(dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size);
decoder->m_buf_pos += size;
return 0;
}
bool SPngDecoder::readHeader()
{
volatile bool result = false;
close();
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32);
if (!ctx)
{
spng_ctx_free(ctx);
return false;
}
m_ctx = ctx;
if (!m_buf.empty())
spng_set_png_stream((struct spng_ctx *)m_ctx, (spng_rw_fn *)readDataFromBuf, this);
else
{
m_f = fopen(m_filename.c_str(), "rb");
if (m_f)
{
spng_set_png_file(ctx, m_f);
}
}
if (!m_buf.empty() || m_f)
{
struct spng_ihdr ihdr;
int ret = spng_get_ihdr(ctx, &ihdr);
if (ret == SPNG_OK)
{
m_width = static_cast<int>(ihdr.width);
m_height = static_cast<int>(ihdr.height);
m_color_type = ihdr.color_type;
m_bit_depth = ihdr.bit_depth;
if (ihdr.bit_depth <= 8 || ihdr.bit_depth == 16)
{
int num_trans;
switch (ihdr.color_type)
{
case SPNG_COLOR_TYPE_TRUECOLOR:
case SPNG_COLOR_TYPE_INDEXED:
struct spng_trns trns;
num_trans = !spng_get_trns(ctx, &trns);
if (num_trans > 0)
m_type = CV_8UC4;
else
m_type = CV_8UC3;
break;
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
m_type = CV_8UC4;
break;
default:
m_type = CV_8UC1;
}
if (ihdr.bit_depth == 16)
m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
result = true;
}
}
}
return result;
}
bool SPngDecoder::readData(Mat &img)
{
volatile bool result = false;
bool color = img.channels() > 1;
struct spng_ctx *png_ptr = (struct spng_ctx *)m_ctx;
if (m_ctx && m_width && m_height)
{
int fmt = SPNG_FMT_PNG;
struct spng_trns trns;
int have_trns = spng_get_trns((struct spng_ctx *)m_ctx, &trns);
int decode_flags = 0;
if (have_trns == SPNG_OK)
{
decode_flags = SPNG_DECODE_TRNS;
}
if (img.channels() == 4)
{
if (m_color_type == SPNG_COLOR_TYPE_TRUECOLOR ||
m_color_type == SPNG_COLOR_TYPE_INDEXED ||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE)
fmt = m_bit_depth == 16 ? SPNG_FMT_GA16 : SPNG_FMT_GA8;
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
{
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
}
else
fmt = SPNG_FMT_RGBA8;
}
if (img.channels() == 3)
{
fmt = SPNG_FMT_RGB8;
if ((m_color_type == SPNG_COLOR_TYPE_GRAYSCALE || m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) &&
m_bit_depth == 16)
fmt = SPNG_FMT_RGB8;
else if (m_bit_depth == 16)
fmt = SPNG_FMT_PNG;
}
else if (img.channels() == 1)
{
if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth <= 8)
fmt = SPNG_FMT_G8;
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth == 16)
{
if (img.depth() == CV_8U || img.depth() == CV_8S)
{
fmt = SPNG_FMT_RGB8;
}
else
{
fmt = SPNG_FMT_PNG;
}
}
else if (m_color_type == SPNG_COLOR_TYPE_INDEXED ||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR)
{
if (img.depth() == CV_8U || img.depth() == CV_8S)
{
fmt = SPNG_FMT_RGB8;
}
else
{
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGB8;
}
}
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || fmt == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
{
if (img.depth() == CV_8U || img.depth() == CV_8S)
{
fmt = SPNG_FMT_RGB8;
}
else
{
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
}
}
else
fmt = SPNG_FMT_RGB8;
}
size_t image_width, image_size = 0;
int ret = spng_decoded_image_size(png_ptr, fmt, &image_size);
struct spng_ihdr ihdr;
spng_get_ihdr(png_ptr, &ihdr);
if (ret == SPNG_OK)
{
image_width = image_size / m_height;
ret = spng_decode_image(png_ptr, nullptr, 0, fmt, SPNG_DECODE_PROGRESSIVE | decode_flags);
if (ret == SPNG_OK)
{
struct spng_row_info row_info{};
// If user wants to read image as grayscale(IMREAD_GRAYSCALE), but image format is not
// decode image then convert to grayscale
if (!color && (fmt == SPNG_FMT_RGB8 || fmt == SPNG_FMT_RGBA8 || fmt == SPNG_FMT_RGBA16))
{
if (ihdr.interlace_method == 0)
{
AutoBuffer<unsigned char> buffer;
buffer.allocate(image_width);
if (fmt == SPNG_FMT_RGB8)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
spngCvt_BGR2Gray_8u_C3C1R(
buffer.data(),
0,
img.data + row_info.row_num * img.step,
0, Size(m_width, 1), 2);
} while (ret == SPNG_OK);
}
else if (fmt == SPNG_FMT_RGBA8)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
spngCvt_BGRA2Gray_8u_C4C1R(
buffer.data(),
0,
img.data + row_info.row_num * img.step,
0, Size(m_width, 1), 2);
} while (ret == SPNG_OK);
}
else if (fmt == SPNG_FMT_RGBA16)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
spngCvt_BGRA2Gray_16u_CnC1R(
reinterpret_cast<const ushort *>(buffer.data()), 0,
reinterpret_cast<ushort *>(img.data + row_info.row_num * img.step),
0, Size(m_width, 1),
4, 2);
} while (ret == SPNG_OK);
}
}
else
{
AutoBuffer<unsigned char> imageBuffer(image_size);
ret = spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0);
int step = m_width * img.channels();
if (fmt == SPNG_FMT_RGB8)
{
spngCvt_BGR2Gray_8u_C3C1R(
imageBuffer.data(),
step,
img.data,
step, Size(m_width, m_height), 2);
}
else if (fmt == SPNG_FMT_RGBA8)
{
spngCvt_BGRA2Gray_8u_C4C1R(
imageBuffer.data(),
step,
img.data,
step, Size(m_width, m_height), 2);
}
else if (fmt == SPNG_FMT_RGBA16)
{
spngCvt_BGRA2Gray_16u_CnC1R(
reinterpret_cast<const ushort *>(imageBuffer.data()), step / 3,
reinterpret_cast<ushort *>(img.data),
step / 3, Size(m_width, m_height),
4, 2);
}
}
}
else if (color)
{ // RGB -> BGR, convert row by row if png is non-interlaced, otherwise convert image as one
int step = m_width * img.channels();
AutoBuffer<uchar *> _buffer(m_height);
uchar **buffer = _buffer.data();
for (int y = 0; y < m_height; y++)
{
buffer[y] = img.data + y * img.step;
}
if (img.channels() == 4 && m_bit_depth == 16)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0,
Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(img.data), step * 2, reinterpret_cast<ushort *>(img.data), step * 2, Size(m_width, m_height));
}
}
else if (img.channels() == 4)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGBA2BGRA_8u_C4R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGBA2BGRA_8u_C4R(img.data, step, img.data, step, Size(m_width, m_height));
}
}
else if (fmt == SPNG_FMT_PNG)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(img.data), step,
reinterpret_cast<ushort *>(img.data), step, Size(m_width, m_height));
}
}
else
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGB2BGR_8u_C3R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGB2BGR_8u_C3R(img.data, step, img.data, step, Size(m_width, m_height));
}
}
}
else
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, img.data + row_info.row_num * image_width, image_width);
} while (ret == SPNG_OK);
}
}
if (ret == SPNG_EOI)
{
ret = spng_decode_chunks(png_ptr);
if(ret == SPNG_OK) result = true;
struct spng_exif exif_s{};
ret = spng_get_exif(png_ptr, &exif_s);
if (ret == SPNG_OK)
{
if (exif_s.data && exif_s.length > 0)
{
result = m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length);
}
}
}
}
}
return result;
}
/////////////////////// SPngEncoder ///////////////////
SPngEncoder::SPngEncoder()
{
m_description = "Portable Network Graphics files (*.png)";
m_buf_supported = true;
}
SPngEncoder::~SPngEncoder()
{
}
bool SPngEncoder::isFormatSupported(int depth) const
{
return depth == CV_8U || depth == CV_16U;
}
ImageEncoder SPngEncoder::newEncoder() const
{
return makePtr<SPngEncoder>();
}
int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length)
{
CV_UNUSED(ctx);
SPngEncoder *encoder = (SPngEncoder *)(user);
CV_Assert(encoder && encoder->m_buf);
size_t cursz = encoder->m_buf->size();
encoder->m_buf->resize(cursz + length);
memcpy(&(*encoder->m_buf)[cursz], dst_src, length);
return 0;
}
bool SPngEncoder::write(const Mat &img, const std::vector<int> &params)
{
int fmt;
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER);
FILE *volatile f = 0;
int width = img.cols, height = img.rows;
int depth = img.depth(), channels = img.channels();
volatile bool result = false;
if (depth != CV_8U && depth != CV_16U)
return false;
if (ctx)
{
struct spng_ihdr ihdr = {};
ihdr.height = height;
ihdr.width = width;
int compression_level = -1; // Invalid value to allow setting 0-9 as valid
int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
bool isBilevel = false;
for (size_t i = 0; i < params.size(); i += 2)
{
if (params[i] == IMWRITE_PNG_COMPRESSION)
{
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
compression_level = params[i + 1];
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
}
if (params[i] == IMWRITE_PNG_STRATEGY)
{
compression_strategy = params[i + 1];
compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
}
if (params[i] == IMWRITE_PNG_BILEVEL)
{
isBilevel = params[i + 1] != 0;
}
}
fmt = channels == 1 ? SPNG_COLOR_TYPE_GRAYSCALE : channels == 3 ? SPNG_COLOR_TYPE_TRUECOLOR
: SPNG_COLOR_TYPE_TRUECOLOR_ALPHA;
ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16;
ihdr.color_type = fmt;
ihdr.interlace_method = SPNG_INTERLACE_NONE;
ihdr.filter_method = SPNG_FILTER_NONE;
ihdr.compression_method = 0;
spng_set_ihdr(ctx, &ihdr);
if (m_buf)
{
spng_set_png_stream(ctx, (spng_rw_fn *)writeDataToBuf, this);
}
else
{
f = fopen(m_filename.c_str(), "wb");
if (f)
spng_set_png_file(ctx, f);
}
if (m_buf || f)
{
if (compression_level >= 0)
{
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, compression_level);
}
else
{
spng_set_option(ctx, SPNG_FILTER_CHOICE, SPNG_FILTER_CHOICE_SUB);
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, Z_BEST_SPEED);
}
spng_set_option(ctx, SPNG_IMG_COMPRESSION_STRATEGY, compression_strategy);
int ret;
spng_encode_chunks(ctx);
ret = spng_encode_image(ctx, nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE);
if (channels > 1)
{
int error;
if (ret == SPNG_OK)
{
if (depth == CV_16U)
{
AutoBuffer<ushort *> buff2;
buff2.allocate(height);
for (int y = 0; y < height; y++)
buff2[y] = reinterpret_cast<unsigned short *>(img.data + y * img.step);
AutoBuffer<ushort> _buffer;
_buffer.allocate(width * channels * 2);
for (int y = 0; y < height; y++)
{
if (channels == 3)
{
icvCvt_BGR2RGB_16u_C3R(buff2[y], 0,
_buffer.data(), 0, Size(width, 1));
}
else if (channels == 4)
{
icvCvt_BGRA2RGBA_16u_C4R(buff2[y], 0,
_buffer.data(), 0, Size(width, 1));
}
error = spng_encode_row(ctx, _buffer.data(), width * channels * 2);
if (error)
break;
}
}
else
{
AutoBuffer<uchar *> buff;
buff.allocate(height);
for (int y = 0; y < height; y++)
buff[y] = img.data + y * img.step;
AutoBuffer<uchar> _buffer;
_buffer.allocate(width * channels);
for (int y = 0; y < height; y++)
{
if (channels == 3)
{
icvCvt_BGR2RGB_8u_C3R(buff[y], 0, _buffer.data(), 0, Size(width, 1));
}
else if (channels == 4)
{
icvCvt_BGRA2RGBA_8u_C4R(buff[y], 0, _buffer.data(), 0, Size(width, 1));
}
error = spng_encode_row(ctx, _buffer.data(), width * channels);
if (error)
break;
}
}
if (error == SPNG_EOI)
{ // success
spng_encode_chunks(ctx);
ret = SPNG_OK;
}
}
}
else
{
int error;
for (int y = 0; y < height; y++)
{
error = spng_encode_row(ctx, img.data + y * img.step, width * channels * (depth == CV_16U ? 2 : 1));
if (error)
break;
}
if (error == SPNG_EOI)
{ // success
spng_encode_chunks(ctx);
ret = SPNG_OK;
}
}
if (ret == SPNG_OK)
result = true;
}
}
spng_ctx_free(ctx);
if (f)
fclose((FILE *)f);
return result;
}
}
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb)
{
int i;
for (; size.height--; gray += gray_step)
{
double cBGR0 = 0.1140441895;
double cBGR2 = 0.2989807129;
if (_swap_rb)
std::swap(cBGR0, cBGR2);
for (i = 0; i < size.width; i++, bgr += 3)
{
int t = static_cast<int>(cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]);
gray[i] = (uchar)t;
}
bgr += bgr_step - size.width * 3;
}
}
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb)
{
int i;
for (; size.height--; gray += gray_step)
{
double cBGR0 = 0.1140441895;
double cBGR2 = 0.2989807129;
if (_swap_rb)
std::swap(cBGR0, cBGR2);
for (i = 0; i < size.width; i++, bgra += 4)
{
int t = cBGR0 * bgra[0] + 0.5869750977 * bgra[1] + cBGR2 * bgra[2];
gray[i] = (uchar)t;
}
bgra += rgba_step - size.width * 4;
}
}
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
ushort *gray, int gray_step,
cv::Size size, int ncn, int _swap_rb)
{
int i;
for (; size.height--; gray += gray_step)
{
double cBGR0 = 0.1140441895;
double cBGR2 = 0.2989807129;
if (_swap_rb)
std::swap(cBGR0, cBGR2);
for (i = 0; i < size.width; i++, bgr += ncn)
{
int t = cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2];
gray[i] = (ushort)t;
}
bgr += bgr_step - size.width * ncn;
}
}
#endif
/* End of file. */
+60
View File
@@ -0,0 +1,60 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef _GRFMT_SPNG_H_
#define _GRFMT_SPNG_H_
#ifdef HAVE_SPNG
#include "grfmt_base.hpp"
#include "bitstrm.hpp"
namespace cv
{
class SPngDecoder CV_FINAL : public BaseImageDecoder
{
public:
SPngDecoder();
virtual ~SPngDecoder();
bool readData( Mat& img ) CV_OVERRIDE;
bool readHeader() CV_OVERRIDE;
void close();
ImageDecoder newDecoder() const CV_OVERRIDE;
protected:
static int readDataFromBuf(void* sp_ctx, void *user, void* dst, size_t size);
int m_bit_depth;
void* m_ctx;
FILE* m_f;
int m_color_type;
size_t m_buf_pos;
};
class SPngEncoder CV_FINAL : public BaseImageEncoder
{
public:
SPngEncoder();
virtual ~SPngEncoder();
bool isFormatSupported( int depth ) const CV_OVERRIDE;
bool write( const Mat& img, const std::vector<int>& params ) CV_OVERRIDE;
ImageEncoder newEncoder() const CV_OVERRIDE;
protected:
static int writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length);
};
}
#endif
#endif/*_GRFMT_PNG_H_*/
+230 -31
View File
@@ -238,7 +238,6 @@ public:
bool TiffDecoder::readHeader()
{
bool result = false;
TIFF* tif = static_cast<TIFF*>(m_tif.get());
if (!tif)
{
@@ -312,6 +311,18 @@ bool TiffDecoder::readHeader()
result = true;
break;
}
case 4:
//support 4-bit palette.
if (photometric == PHOTOMETRIC_PALETTE)
{
CV_Check((int)sample_format, sample_format == SAMPLEFORMAT_UINT || sample_format == SAMPLEFORMAT_INT, "");
int depth = sample_format == SAMPLEFORMAT_INT ? CV_8S : CV_8U;
m_type = CV_MAKETYPE(depth, 3);
result = true;
}
else
CV_Error(cv::Error::StsError, "bitsperpixel value is 4 should be palette.");
break;
case 8:
{
//Palette color, the value of the component is used as an index into the red,
@@ -425,18 +436,15 @@ static void fixOrientationFull(Mat &img, int orientation)
* For 8 bit some corrections are done by TIFFReadRGBAStrip/Tile already.
* Not so for 16/32/64 bit.
*/
static void fixOrientation(Mat &img, uint16 orientation, int dst_bpp)
static void fixOrientation(Mat &img, uint16 orientation, bool isOrientationFull)
{
switch(dst_bpp) {
case 8:
fixOrientationPartial(img, orientation);
break;
case 16:
case 32:
case 64:
fixOrientationFull(img, orientation);
break;
if( isOrientationFull )
{
fixOrientationFull(img, orientation);
}
else
{
fixOrientationPartial(img, orientation);
}
}
@@ -620,17 +628,7 @@ bool TiffDecoder::readData( Mat& img )
(img_orientation == ORIENTATION_BOTRIGHT || img_orientation == ORIENTATION_RIGHTBOT ||
img_orientation == ORIENTATION_BOTLEFT || img_orientation == ORIENTATION_LEFTBOT);
int wanted_channels = normalizeChannelsNumber(img.channels());
if (dst_bpp == 8)
{
char errmsg[1024];
if (!TIFFRGBAImageOK(tif, errmsg))
{
CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg);
close();
return false;
}
}
bool doReadScanline = false;
uint32 tile_width0 = m_width, tile_height0 = 0;
@@ -660,21 +658,123 @@ bool TiffDecoder::readData( Mat& img )
const uint64_t MAX_TILE_SIZE = (CV_BIG_UINT(1) << 30);
CV_CheckLE((int)ncn, 4, "");
CV_CheckLE((int)bpp, 64, "");
CV_Assert(((uint64_t)tile_width0 * tile_height0 * ncn * std::max(1, (int)(bpp / bitsPerByte)) < MAX_TILE_SIZE) && "TIFF tile size is too large: >= 1Gb");
if (dst_bpp == 8)
{
// we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA
bpp = 8;
ncn = 4;
const int _ncn = 4; // Read RGBA
const int _bpp = 8; // Read 8bit
// if buffer_size(as 32bit RGBA) >= MAX_TILE_SIZE*95%,
// we will use TIFFReadScanline function.
if (
(uint64_t)tile_width0 * tile_height0 * _ncn * std::max(1, (int)(_bpp / bitsPerByte))
>=
( (uint64_t) MAX_TILE_SIZE * 95 / 100)
)
{
uint16_t planerConfig = (uint16)-1;
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planerConfig));
doReadScanline = (!is_tiled) // no tile
&&
( ( ncn == 1 ) || ( ncn == 3 ) || ( ncn == 4 ) )
&&
( ( bpp == 8 ) || ( bpp == 16 ) )
&&
(tile_height0 == (uint32_t) m_height) // single strip
&&
(
(photometric == PHOTOMETRIC_MINISWHITE)
||
(photometric == PHOTOMETRIC_MINISBLACK)
||
(photometric == PHOTOMETRIC_RGB)
)
&&
(planerConfig != PLANARCONFIG_SEPARATE);
// Currently only EXTRASAMPLE_ASSOCALPHA is supported.
if ( doReadScanline && ( ncn == 4 ) )
{
uint16_t extra_samples_num;
uint16_t *extra_samples = NULL;
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_samples_num, &extra_samples ));
doReadScanline = ( extra_samples_num == 1 ) && ( extra_samples[0] == EXTRASAMPLE_ASSOCALPHA );
}
}
if ( !doReadScanline )
{
// we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA
bpp = 8;
ncn = 4;
char errmsg[1024];
if (!TIFFRGBAImageOK(tif, errmsg))
{
CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg);
close();
return false;
}
}
}
else if (dst_bpp == 16)
{
// if buffer_size >= MAX_TILE_SIZE*95%,
// we will use TIFFReadScanline function.
if (
(uint64_t)tile_width0 * tile_height0 * ncn * std::max(1, (int)(bpp / bitsPerByte))
>=
MAX_TILE_SIZE * 95 / 100
)
{
uint16_t planerConfig = (uint16)-1;
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planerConfig));
doReadScanline = (!is_tiled) // no tile
&&
( ( ncn == 1 ) || ( ncn == 3 ) || ( ncn == 4 ) )
&&
( ( bpp == 8 ) || ( bpp == 16 ) )
&&
(tile_height0 == (uint32_t) m_height) // single strip
&&
(
(photometric == PHOTOMETRIC_MINISWHITE)
||
(photometric == PHOTOMETRIC_MINISBLACK)
||
(photometric == PHOTOMETRIC_RGB)
)
&&
(planerConfig != PLANARCONFIG_SEPARATE);
// Currently only EXTRASAMPLE_ASSOCALPHA is supported.
if ( doReadScanline && ( ncn == 4 ) )
{
uint16_t extra_samples_num;
uint16_t *extra_samples = NULL;
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_samples_num, &extra_samples ));
doReadScanline = ( extra_samples_num == 1 ) && ( extra_samples[0] == EXTRASAMPLE_ASSOCALPHA );
}
}
}
else if (dst_bpp == 32 || dst_bpp == 64)
{
CV_Assert(ncn == img.channels());
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP));
}
if ( doReadScanline )
{
// Read each scanlines.
tile_height0 = 1;
}
const size_t src_buffer_bytes_per_row = divUp(static_cast<size_t>(ncn * tile_width0 * bpp), static_cast<size_t>(bitsPerByte));
const size_t src_buffer_size = tile_height0 * src_buffer_bytes_per_row;
CV_CheckLT(src_buffer_size, MAX_TILE_SIZE, "buffer_size is too large: >= 1Gb");
const size_t src_buffer_unpacked_bytes_per_row = divUp(static_cast<size_t>(ncn * tile_width0 * dst_bpp), static_cast<size_t>(bitsPerByte));
const size_t src_buffer_unpacked_size = tile_height0 * src_buffer_unpacked_bytes_per_row;
const bool needsUnpacking = (bpp < dst_bpp);
@@ -682,8 +782,20 @@ bool TiffDecoder::readData( Mat& img )
uchar* src_buffer = _src_buffer.data();
AutoBuffer<uchar> _src_buffer_unpacked(needsUnpacking ? src_buffer_unpacked_size : 0);
uchar* src_buffer_unpacked = needsUnpacking ? _src_buffer_unpacked.data() : nullptr;
if ( doReadScanline )
{
CV_CheckGE(src_buffer_size,
static_cast<size_t>(TIFFScanlineSize(tif)),
"src_buffer_size is smaller than TIFFScanlineSize().");
}
int tileidx = 0;
#define MAKE_FLAG(a,b) ( (a << 8) | b )
const int convert_flag = MAKE_FLAG( ncn, wanted_channels );
const bool isNeedConvert16to8 = ( doReadScanline ) && ( bpp == 16 ) && ( dst_bpp == 8);
for (int y = 0; y < m_height; y += (int)tile_height0)
{
int tile_height = std::min((int)tile_height0, m_height - y);
@@ -699,7 +811,29 @@ bool TiffDecoder::readData( Mat& img )
case 8:
{
uchar* bstart = src_buffer;
if (!is_tiled)
if (doReadScanline)
{
CV_TIFF_CHECK_CALL((int)TIFFReadScanline(tif, (uint32*)src_buffer, y) >= 0);
if ( isNeedConvert16to8 )
{
// Convert buffer image from 16bit to 8bit.
int ix;
for ( ix = 0 ; ix < tile_width * ncn - 4; ix += 4 )
{
src_buffer[ ix ] = src_buffer[ ix * 2 + 1 ];
src_buffer[ ix + 1 ] = src_buffer[ ix * 2 + 3 ];
src_buffer[ ix + 2 ] = src_buffer[ ix * 2 + 5 ];
src_buffer[ ix + 3 ] = src_buffer[ ix * 2 + 7 ];
}
for ( ; ix < tile_width * ncn ; ix ++ )
{
src_buffer[ ix ] = src_buffer[ ix * 2 + 1];
}
}
}
else if (!is_tiled)
{
CV_TIFF_CHECK_CALL(TIFFReadRGBAStrip(tif, y, (uint32*)src_buffer));
}
@@ -710,9 +844,65 @@ bool TiffDecoder::readData( Mat& img )
bstart += (tile_height0 - tile_height) * tile_width0 * 4;
}
uchar* img_line_buffer = (uchar*) img.ptr(y, 0);
for (int i = 0; i < tile_height; i++)
{
if (color)
if (doReadScanline)
{
switch ( convert_flag )
{
case MAKE_FLAG( 1, 1 ): // GRAY to GRAY
memcpy( (void*) img_line_buffer,
(void*) bstart,
tile_width * sizeof(uchar) );
break;
case MAKE_FLAG( 1, 3 ): // GRAY to BGR
icvCvt_Gray2BGR_8u_C1C3R( bstart, 0,
img_line_buffer, 0,
Size(tile_width, 1) );
break;
case MAKE_FLAG( 3, 1): // RGB to GRAY
icvCvt_BGR2Gray_8u_C3C1R( bstart, 0,
img_line_buffer, 0,
Size(tile_width, 1) );
break;
case MAKE_FLAG( 3, 3 ): // RGB to BGR
icvCvt_BGR2RGB_8u_C3R( bstart, 0,
img_line_buffer, 0,
Size(tile_width, 1) );
break;
case MAKE_FLAG( 4, 1 ): // RGBA to GRAY
icvCvt_BGRA2Gray_8u_C4C1R( bstart, 0,
img_line_buffer, 0,
Size(tile_width, 1) );
break;
case MAKE_FLAG( 4, 3 ): // RGBA to BGR
icvCvt_BGRA2BGR_8u_C4C3R( bstart, 0,
img_line_buffer, 0,
Size(tile_width, 1), 2 );
break;
case MAKE_FLAG( 4, 4 ): // RGBA to BGRA
icvCvt_BGRA2RGBA_8u_C4R(bstart, 0,
img_line_buffer, 0,
Size(tile_width, 1) );
break;
default:
CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported convertion :"
<< " bpp = " << bpp << " ncn = " << (int)ncn
<< " wanted_channels =" << wanted_channels );
break;
}
#undef MAKE_FLAG
}
else if (color)
{
if (wanted_channels == 4)
{
@@ -741,7 +931,11 @@ bool TiffDecoder::readData( Mat& img )
case 16:
{
if (!is_tiled)
if (doReadScanline)
{
CV_TIFF_CHECK_CALL((int)TIFFReadScanline(tif, (uint32*)src_buffer, y) >= 0);
}
else if (!is_tiled)
{
CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, (uint32*)src_buffer, src_buffer_size) >= 0);
}
@@ -862,7 +1056,11 @@ bool TiffDecoder::readData( Mat& img )
}
if (bpp < dst_bpp)
img *= (1<<(dst_bpp-bpp));
fixOrientation(img, img_orientation, dst_bpp);
// If TIFFReadRGBA* function is used -> fixOrientationPartial().
// Otherwise -> fixOrientationFull().
fixOrientation(img, img_orientation,
( ( dst_bpp != 8 ) && ( !doReadScanline ) ) );
}
if (m_hdr && depth >= CV_32F)
@@ -887,6 +1085,7 @@ TiffEncoder::~TiffEncoder()
ImageEncoder TiffEncoder::newEncoder() const
{
cv_tiffSetErrorHandler();
return makePtr<TiffEncoder>();
}
+2
View File
@@ -126,6 +126,8 @@ bool WebPDecoder::readHeader()
WebPBitstreamFeatures features;
if (VP8_STATUS_OK == WebPGetFeatures(header, sizeof(header), &features))
{
CV_CheckEQ(features.has_animation, 0, "WebP backend does not support animated webp images");
m_width = features.width;
m_height = features.height;
+1
View File
@@ -49,6 +49,7 @@
#include "grfmt_pxm.hpp"
#include "grfmt_pfm.hpp"
#include "grfmt_tiff.hpp"
#include "grfmt_spng.hpp"
#include "grfmt_png.hpp"
#include "grfmt_jpeg2000.hpp"
#include "grfmt_jpeg2000_openjpeg.hpp"
+411 -52
View File
@@ -54,6 +54,8 @@
#include <cerrno>
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/core/utils/configuration.private.hpp>
#include <opencv2/imgcodecs.hpp>
/****************************************************************************************\
@@ -167,7 +169,10 @@ struct ImageCodecInitializer
decoders.push_back( makePtr<TiffDecoder>() );
encoders.push_back( makePtr<TiffEncoder>() );
#endif
#ifdef HAVE_PNG
#ifdef HAVE_SPNG
decoders.push_back( makePtr<SPngDecoder>() );
encoders.push_back( makePtr<SPngEncoder>() );
#elif defined(HAVE_PNG)
decoders.push_back( makePtr<PngDecoder>() );
encoders.push_back( makePtr<PngEncoder>() );
#endif
@@ -658,57 +663,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
static
size_t imcount_(const String& filename, int flags)
{
/// Search for the relevant decoder to handle the imagery
ImageDecoder decoder;
#ifdef HAVE_GDAL
if (flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
decoder = GdalDecoder().newDecoder();
try{
ImageCollection collection(filename, flags);
return collection.size();
} catch(cv::Exception const& e) {
// Reading header or finding decoder for the filename is failed
std::cerr << "imcount_('" << filename << "'): can't read header or can't find decoder: " << e.what() << std::endl << std::flush;
}
else {
#else
CV_UNUSED(flags);
#endif
decoder = findDecoder(filename);
#ifdef HAVE_GDAL
}
#endif
/// if no decoder was found, return nothing.
if (!decoder) {
return 0;
}
/// set the filename in the driver
decoder->setSource(filename);
// read the header to make sure it succeeds
try
{
// read the header to make sure it succeeds
if (!decoder->readHeader())
return 0;
}
catch (const cv::Exception& e)
{
std::cerr << "imcount_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush;
return 0;
}
catch (...)
{
std::cerr << "imcount_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush;
return 0;
}
size_t result = 1;
while (decoder->nextPage())
{
++result;
}
return result;
return 0;
}
size_t imcount(const String& filename, int flags)
@@ -754,7 +716,9 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
}
encoder->setDestination( filename );
CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2);
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
bool code = false;
try
{
@@ -967,6 +931,157 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
return *dst;
}
static bool
imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int count)
{
CV_Assert(!buf.empty());
CV_Assert(buf.isContinuous());
CV_Assert(buf.checkVector(1, CV_8U) > 0);
Mat buf_row = buf.reshape(1, 1); // decoders expects single row, avoid issues with vector columns
String filename;
ImageDecoder decoder = findDecoder(buf_row);
if (!decoder)
return 0;
if (count < 0) {
count = std::numeric_limits<int>::max();
}
if (!decoder->setSource(buf_row))
{
filename = tempfile();
FILE* f = fopen(filename.c_str(), "wb");
if (!f)
return 0;
size_t bufSize = buf_row.total() * buf.elemSize();
if (fwrite(buf_row.ptr(), 1, bufSize, f) != bufSize)
{
fclose(f);
CV_Error(Error::StsError, "failed to write image data to temporary file");
}
if (fclose(f) != 0)
{
CV_Error(Error::StsError, "failed to write image data to temporary file");
}
decoder->setSource(filename);
}
// read the header to make sure it succeeds
bool success = false;
try
{
// read the header to make sure it succeeds
if (decoder->readHeader())
success = true;
}
catch (const cv::Exception& e)
{
std::cerr << "imreadmulti_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush;
}
catch (...)
{
std::cerr << "imreadmulti_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush;
}
int current = start;
while (success && current > 0)
{
if (!decoder->nextPage())
{
success = false;
break;
}
--current;
}
if (!success)
{
decoder.release();
if (!filename.empty())
{
if (0 != remove(filename.c_str()))
{
std::cerr << "unable to remove temporary file:" << filename << std::endl << std::flush;
}
}
return 0;
}
while (current < count)
{
// grab the decoded type
int type = decoder->type();
if ((flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED)
{
if ((flags & IMREAD_ANYDEPTH) == 0)
type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));
if ((flags & IMREAD_COLOR) != 0 ||
((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1))
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);
else
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1);
}
// established the required input image size
Size size = validateInputImageSize(Size(decoder->width(), decoder->height()));
// read the image data
Mat mat(size.height, size.width, type);
success = false;
try
{
if (decoder->readData(mat))
success = true;
}
catch (const cv::Exception& e)
{
std::cerr << "imreadmulti_('" << filename << "'): can't read data: " << e.what() << std::endl << std::flush;
}
catch (...)
{
std::cerr << "imreadmulti_('" << filename << "'): can't read data: unknown exception" << std::endl << std::flush;
}
if (!success)
break;
// optionally rotate the data if EXIF' orientation flag says so
if ((flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED)
{
ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
}
mats.push_back(mat);
if (!decoder->nextPage())
{
break;
}
++current;
}
if (!filename.empty())
{
if (0 != remove(filename.c_str()))
{
std::cerr << "unable to remove temporary file:" << filename << std::endl << std::flush;
}
}
if (!success)
mats.clear();
return !mats.empty();
}
bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector<Mat>& mats)
{
CV_TRACE_FUNCTION();
Mat buf = _buf.getMat();
return imdecodemulti_(buf, flags, mats, 0, -1);
}
bool imencode( const String& ext, InputArray _image,
std::vector<uchar>& buf, const std::vector<int>& params )
{
@@ -990,6 +1105,9 @@ bool imencode( const String& ext, InputArray _image,
image = temp;
}
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
bool code;
if( encoder->setDestination(buf) )
{
@@ -1032,6 +1150,247 @@ bool haveImageWriter( const String& filename )
return !encoder.empty();
}
class ImageCollection::Impl {
public:
Impl() = default;
Impl(const std::string& filename, int flags);
void init(String const& filename, int flags);
size_t size() const;
Mat& at(int index);
Mat& operator[](int index);
void releaseCache(int index);
ImageCollection::iterator begin(ImageCollection* ptr);
ImageCollection::iterator end(ImageCollection* ptr);
Mat read();
int width() const;
int height() const;
bool readHeader();
Mat readData();
bool advance();
int currentIndex() const;
void reset();
private:
String m_filename;
int m_flags{};
std::size_t m_size{};
int m_width{};
int m_height{};
int m_current{};
std::vector<cv::Mat> m_pages;
ImageDecoder m_decoder;
};
ImageCollection::Impl::Impl(std::string const& filename, int flags) {
this->init(filename, flags);
}
void ImageCollection::Impl::init(String const& filename, int flags) {
m_filename = filename;
m_flags = flags;
#ifdef HAVE_GDAL
if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
m_decoder = GdalDecoder().newDecoder();
}
else {
#endif
m_decoder = findDecoder(filename);
#ifdef HAVE_GDAL
}
#endif
CV_Assert(m_decoder);
m_decoder->setSource(filename);
CV_Assert(m_decoder->readHeader());
// count the pages of the image collection
size_t count = 1;
while(m_decoder->nextPage()) count++;
m_size = count;
m_pages.resize(m_size);
// Reinitialize the decoder because we advanced to the last page while counting the pages of the image
#ifdef HAVE_GDAL
if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
m_decoder = GdalDecoder().newDecoder();
}
else {
#endif
m_decoder = findDecoder(m_filename);
#ifdef HAVE_GDAL
}
#endif
m_decoder->setSource(m_filename);
m_decoder->readHeader();
}
size_t ImageCollection::Impl::size() const { return m_size; }
Mat ImageCollection::Impl::read() {
auto result = this->readHeader();
if(!result) {
return {};
}
return this->readData();
}
int ImageCollection::Impl::width() const {
return m_width;
}
int ImageCollection::Impl::height() const {
return m_height;
}
bool ImageCollection::Impl::readHeader() {
bool status = m_decoder->readHeader();
m_width = m_decoder->width();
m_height = m_decoder->height();
return status;
}
// readHeader must be called before calling this method
Mat ImageCollection::Impl::readData() {
int type = m_decoder->type();
if ((m_flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && m_flags != IMREAD_UNCHANGED) {
if ((m_flags & IMREAD_ANYDEPTH) == 0)
type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));
if ((m_flags & IMREAD_COLOR) != 0 ||
((m_flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1))
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);
else
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1);
}
// established the required input image size
Size size = validateInputImageSize(Size(m_width, m_height));
Mat mat(size.height, size.width, type);
bool success = false;
try {
if (m_decoder->readData(mat))
success = true;
}
catch (const cv::Exception &e) {
std::cerr << "ImageCollection class: can't read data: " << e.what() << std::endl << std::flush;
}
catch (...) {
std::cerr << "ImageCollection class:: can't read data: unknown exception" << std::endl << std::flush;
}
if (!success)
return cv::Mat();
if ((m_flags & IMREAD_IGNORE_ORIENTATION) == 0 && m_flags != IMREAD_UNCHANGED) {
ApplyExifOrientation(m_decoder->getExifTag(ORIENTATION), mat);
}
return mat;
}
bool ImageCollection::Impl::advance() { ++m_current; return m_decoder->nextPage(); }
int ImageCollection::Impl::currentIndex() const { return m_current; }
ImageCollection::iterator ImageCollection::Impl::begin(ImageCollection* ptr) { return ImageCollection::iterator(ptr); }
ImageCollection::iterator ImageCollection::Impl::end(ImageCollection* ptr) { return ImageCollection::iterator(ptr, static_cast<int>(this->size())); }
void ImageCollection::Impl::reset() {
m_current = 0;
#ifdef HAVE_GDAL
if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
m_decoder = GdalDecoder().newDecoder();
}
else {
#endif
m_decoder = findDecoder(m_filename);
#ifdef HAVE_GDAL
}
#endif
m_decoder->setSource(m_filename);
m_decoder->readHeader();
}
Mat& ImageCollection::Impl::at(int index) {
CV_Assert(index >= 0 && size_t(index) < m_size);
return operator[](index);
}
Mat& ImageCollection::Impl::operator[](int index) {
if(m_pages.at(index).empty()) {
// We can't go backward in multi images. If the page is not in vector yet,
// go back to first page and advance until the desired page and read it into memory
if(m_current != index) {
reset();
for(int i = 0; i != index && advance(); ++i) {}
}
m_pages[index] = read();
}
return m_pages[index];
}
void ImageCollection::Impl::releaseCache(int index) {
CV_Assert(index >= 0 && size_t(index) < m_size);
m_pages[index].release();
}
/* ImageCollection API*/
ImageCollection::ImageCollection() : pImpl(new Impl()) {}
ImageCollection::ImageCollection(const std::string& filename, int flags) : pImpl(new Impl(filename, flags)) {}
void ImageCollection::init(const String& img, int flags) { pImpl->init(img, flags); }
size_t ImageCollection::size() const { return pImpl->size(); }
const Mat& ImageCollection::at(int index) { return pImpl->at(index); }
const Mat& ImageCollection::operator[](int index) { return pImpl->operator[](index); }
void ImageCollection::releaseCache(int index) { pImpl->releaseCache(index); }
Ptr<ImageCollection::Impl> ImageCollection::getImpl() { return pImpl; }
/* Iterator API */
ImageCollection::iterator ImageCollection::begin() { return pImpl->begin(this); }
ImageCollection::iterator ImageCollection::end() { return pImpl->end(this); }
ImageCollection::iterator::iterator(ImageCollection* col) : m_pCollection(col), m_curr(0) {}
ImageCollection::iterator::iterator(ImageCollection* col, int end) : m_pCollection(col), m_curr(end) {}
Mat& ImageCollection::iterator::operator*() {
CV_Assert(m_pCollection);
return m_pCollection->getImpl()->operator[](m_curr);
}
Mat* ImageCollection::iterator::operator->() {
CV_Assert(m_pCollection);
return &m_pCollection->getImpl()->operator[](m_curr);
}
ImageCollection::iterator& ImageCollection::iterator::operator++() {
if(m_pCollection->pImpl->currentIndex() == m_curr) {
m_pCollection->pImpl->advance();
}
m_curr++;
return *this;
}
ImageCollection::iterator ImageCollection::iterator::operator++(int) {
iterator tmp = *this;
++(*this);
return tmp;
}
}
/* End of file. */