mirror of
https://github.com/opencv/opencv.git
synced 2026-10-28 08:35:30 +08:00
imgcodecs(avif): support 16-bit reading/writing with libavif v1.4.0+ (5.x)
This commit is contained in:
1 parent
7b700ba545
commit
905fa09e04
7 files changed
+125
-16
No files matched your search
+1
-5
@@ -1502,11 +1502,7 @@ if(WITH_WEBP OR HAVE_WEBP)
|
||||
endif()
|
||||
|
||||
if(WITH_AVIF OR HAVE_AVIF)
|
||||
if(libavif_VERSION)
|
||||
status(" AVIF:" AVIF_FOUND THEN "${AVIF_LIBRARY} (ver ${libavif_VERSION})" ELSE "NO")
|
||||
else()
|
||||
status(" AVIF:" AVIF_FOUND THEN "${AVIF_LIBRARY}" ELSE "NO")
|
||||
endif()
|
||||
status(" AVIF:" AVIF_FOUND THEN "${AVIF_LIBRARY} (ver ${AVIF_VERSION})" ELSE "NO")
|
||||
endif()
|
||||
|
||||
if(WITH_SPNG)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# AVIF_INCLUDE_DIRS - where to find avif/avif.h, etc.
|
||||
# AVIF_LIBRARIES - List of libraries when using AVIF.
|
||||
# AVIF_FOUND - True if AVIF is found.
|
||||
# AVIF_VERSION - libavif version string (e.g. "1.4.0")
|
||||
#=============================================================================
|
||||
|
||||
# Look for the header file.
|
||||
@@ -40,7 +41,41 @@ else()
|
||||
# all listed variables are TRUE
|
||||
INCLUDE(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(AVIF DEFAULT_MSG AVIF_LIBRARY AVIF_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
SET(AVIF_LIBRARIES ${AVIF_LIBRARY})
|
||||
SET(AVIF_INCLUDE_DIRS ${AVIF_INCLUDE_DIR})
|
||||
if(NOT AVIF_FOUND)
|
||||
UNSET(AVIF_LIBRARIES)
|
||||
UNSET(AVIF_INCLUDE_DIRS)
|
||||
UNSET(AVIF_VERSION)
|
||||
return()
|
||||
endif()
|
||||
|
||||
SET(AVIF_LIBRARIES ${AVIF_LIBRARY})
|
||||
SET(AVIF_INCLUDE_DIRS ${AVIF_INCLUDE_DIR})
|
||||
|
||||
# unset(libavif_VERSION) # For debugging: uncomment to test fallback version extraction when find_package(libavif) cannot find libavif
|
||||
|
||||
if(NOT libavif_VERSION)
|
||||
find_file(AVIF_H NAMES avif/avif.h PATHS ${AVIF_INCLUDE_DIRS} NO_DEFAULT_PATH)
|
||||
|
||||
if(AVIF_H)
|
||||
file(STRINGS "${AVIF_H}" AVIF_MAJOR_LINE REGEX "^#define[ \t]+AVIF_VERSION_MAJOR[ \t]+[0-9]+")
|
||||
file(STRINGS "${AVIF_H}" AVIF_MINOR_LINE REGEX "^#define[ \t]+AVIF_VERSION_MINOR[ \t]+[0-9]+")
|
||||
file(STRINGS "${AVIF_H}" AVIF_PATCH_LINE REGEX "^#define[ \t]+AVIF_VERSION_PATCH[ \t]+[0-9]+")
|
||||
|
||||
string(REGEX REPLACE ".*AVIF_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" AVIF_MAJOR "${AVIF_MAJOR_LINE}")
|
||||
string(REGEX REPLACE ".*AVIF_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" AVIF_MINOR "${AVIF_MINOR_LINE}")
|
||||
string(REGEX REPLACE ".*AVIF_VERSION_PATCH[ \t]+([0-9]+).*" "\\1" AVIF_PATCH "${AVIF_PATCH_LINE}")
|
||||
|
||||
if(AVIF_MAJOR MATCHES "^[0-9]+$" AND AVIF_MINOR MATCHES "^[0-9]+$" AND AVIF_PATCH MATCHES "^[0-9]+$")
|
||||
SET(libavif_VERSION "${AVIF_MAJOR}.${AVIF_MINOR}.${AVIF_PATCH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
UNSET(AVIF_H CACHE)
|
||||
UNSET(AVIF_H)
|
||||
endif()
|
||||
|
||||
if(libavif_VERSION)
|
||||
SET(AVIF_VERSION "${libavif_VERSION}")
|
||||
endif()
|
||||
@@ -60,7 +60,7 @@ endif()
|
||||
# --- libavif (optional) ---
|
||||
|
||||
if(WITH_AVIF)
|
||||
ocv_clear_internal_cache_vars(AVIF_LIBRARY AVIF_INCLUDE_DIR)
|
||||
ocv_clear_internal_cache_vars(AVIF_LIBRARIES AVIF_INCLUDE_DIRS)
|
||||
include(cmake/OpenCVFindAVIF.cmake)
|
||||
if(AVIF_FOUND)
|
||||
set(HAVE_AVIF 1)
|
||||
|
||||
@@ -14,8 +14,11 @@ if(HAVE_WINRT_CX AND NOT WINRT)
|
||||
endif()
|
||||
|
||||
if (HAVE_AVIF)
|
||||
ocv_include_directories(${AVIF_INCLUDE_DIR})
|
||||
list(APPEND GRFMT_LIBS ${AVIF_LIBRARY})
|
||||
ocv_include_directories(${AVIF_INCLUDE_DIRS})
|
||||
list(APPEND GRFMT_LIBS ${AVIF_LIBRARIES})
|
||||
if(NOT (AVIF_VERSION VERSION_LESS "1.4.0")) # libavif v1.4.0+ supports 16 bit images.
|
||||
add_definitions(-DOPENCV_IMGCODECS_AVIF_SUPPORT_16BIT=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_JPEG)
|
||||
|
||||
@@ -115,7 +115,7 @@ enum ImwriteFlags {
|
||||
IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL .
|
||||
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
|
||||
IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95.
|
||||
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_16U. Default is 8.
|
||||
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10, 12 or 16. 16 requires libavif v1.4.0 or later. If >8, it is stored/read as CV_16U. Default is 8.
|
||||
IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and 10(fastest). Default is 9.
|
||||
IMWRITE_JPEGXL_QUALITY = 640,//!< For JPEG XL, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. If set, distance parameter is re-calicurated from quality level automatically. This parameter request libjxl v0.10 or later.
|
||||
IMWRITE_JPEGXL_EFFORT = 641,//!< For JPEG XL, encoder effort/speed level without affecting decoding speed; it is between 1 (fastest) and 10 (slowest). Default is 7.
|
||||
|
||||
@@ -28,6 +28,23 @@
|
||||
namespace cv {
|
||||
namespace {
|
||||
|
||||
static bool isSupportedBitDepth16U(int requestDepth)
|
||||
{
|
||||
if (requestDepth == 10 || requestDepth == 12)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef OPENCV_IMGCODECS_AVIF_SUPPORT_16BIT
|
||||
if (requestDepth == 16)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct AvifImageDeleter {
|
||||
void operator()(avifImage *image) { avifImageDestroy(image); }
|
||||
};
|
||||
@@ -216,6 +233,11 @@ bool AvifDecoder::readHeader() {
|
||||
return false;
|
||||
}
|
||||
decoder_->strictFlags = AVIF_STRICT_DISABLED;
|
||||
|
||||
#ifdef OPENCV_IMGCODECS_AVIF_SUPPORT_16BIT
|
||||
decoder_->imageContentToDecode |= AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS;
|
||||
#endif
|
||||
|
||||
if (!m_buf.empty()) {
|
||||
CV_Assert(m_buf.type() == CV_8UC1);
|
||||
CV_Assert(m_buf.rows == 1);
|
||||
@@ -241,7 +263,7 @@ bool AvifDecoder::readHeader() {
|
||||
channels_ = (decoder_->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3;
|
||||
if (decoder_->alphaPresent) ++channels_;
|
||||
bit_depth_ = decoder_->image->depth;
|
||||
CV_Assert(bit_depth_ == 8 || bit_depth_ == 10 || bit_depth_ == 12);
|
||||
CV_Assert(bit_depth_ == 8 || isSupportedBitDepth16U(bit_depth_));
|
||||
m_type = CV_MAKETYPE(bit_depth_ == 8 ? CV_8U : CV_16U, channels_);
|
||||
is_first_image_ = true;
|
||||
return true;
|
||||
@@ -360,10 +382,10 @@ bool AvifEncoder::writeanimation(const Animation& animation,
|
||||
#endif
|
||||
} else if (params[i] == IMWRITE_AVIF_DEPTH) {
|
||||
bit_depth = value;
|
||||
if ((bit_depth != 8) && (bit_depth !=10) && (bit_depth !=12))
|
||||
if ((bit_depth != 8) && (!isSupportedBitDepth16U(bit_depth)))
|
||||
{
|
||||
bit_depth = 8;
|
||||
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_DEPTH must be 8, 10 or 12. It is fallbacked to %d", value, bit_depth));
|
||||
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_DEPTH must be 8, 10, 12 (or 16 with libavif v1.4.0+). It is fallbacked to %d", value, bit_depth));
|
||||
}
|
||||
} else if (params[i] == IMWRITE_AVIF_SPEED) {
|
||||
speed = std::min(std::max(value,0),10);
|
||||
@@ -385,17 +407,26 @@ bool AvifEncoder::writeanimation(const Animation& animation,
|
||||
#endif
|
||||
encoder_->speed = speed;
|
||||
|
||||
#ifdef OPENCV_IMGCODECS_AVIF_SUPPORT_16BIT
|
||||
if(bit_depth == 16) {
|
||||
encoder_->sampleTransformRecipe = (do_lossless)?
|
||||
AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B: // lossless
|
||||
AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B; // lossy
|
||||
}
|
||||
#endif
|
||||
|
||||
const avifAddImageFlags flag = (animation.frames.size() == 1)
|
||||
? AVIF_ADD_IMAGE_FLAG_SINGLE
|
||||
: AVIF_ADD_IMAGE_FLAG_NONE;
|
||||
std::vector<AvifImageUniquePtr> images;
|
||||
|
||||
for (const cv::Mat &img : animation.frames) {
|
||||
CV_CheckType(
|
||||
img.type(),
|
||||
(bit_depth == 8 && img.depth() == CV_8U) ||
|
||||
((bit_depth == 10 || bit_depth == 12) && img.depth() == CV_16U),
|
||||
(isSupportedBitDepth16U(bit_depth) && img.depth() == CV_16U),
|
||||
"AVIF only supports bit depth of 8 with CV_8U input or "
|
||||
"bit depth of 10 or 12 with CV_16U input");
|
||||
"bit depth of 10, 12 (or 16 with libavif v1.4.0+) with CV_16U input");
|
||||
|
||||
CV_Check(img.channels(),
|
||||
img.channels() == 1 || img.channels() == 3 || img.channels() == 4,
|
||||
|
||||
@@ -304,6 +304,50 @@ INSTANTIATE_TEST_CASE_P(
|
||||
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
|
||||
IMREAD_COLOR, IMREAD_COLOR_RGB})));
|
||||
|
||||
|
||||
#ifdef OPENCV_IMGCODECS_AVIF_SUPPORT_16BIT
|
||||
class Imgcodecs_Avif_16bit : public testing::TestWithParam<int> {};
|
||||
|
||||
TEST_P(Imgcodecs_Avif_16bit, encode_decode)
|
||||
{
|
||||
const int quality = GetParam();
|
||||
const int bit_depth = 16;
|
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "../cv/features2d/tsukuba.png";
|
||||
|
||||
cv::Mat src;
|
||||
EXPECT_NO_THROW(src = cv::imread(filename));
|
||||
ASSERT_FALSE(src.empty());
|
||||
|
||||
EXPECT_NO_THROW(src.convertTo(src, CV_16UC3, 65536.0 / 255.0));
|
||||
|
||||
std::vector<uint8_t> buf;
|
||||
EXPECT_NO_THROW(imencode(".avif", src, buf, {IMWRITE_AVIF_DEPTH, bit_depth, IMWRITE_AVIF_QUALITY, quality}));
|
||||
|
||||
cv::Mat dst;
|
||||
EXPECT_NO_THROW(dst = imdecode(buf, IMREAD_UNCHANGED));
|
||||
ASSERT_FALSE(dst.empty());
|
||||
|
||||
const float norm = cvtest::norm(dst, src, NORM_L2) /
|
||||
src.channels() / src.cols / src.rows /
|
||||
(1 << (bit_depth - 8));
|
||||
|
||||
switch ( quality ) {
|
||||
case 100: EXPECT_EQ(norm, 0); break; // lossless
|
||||
case 50: EXPECT_LE(norm, 10); break;
|
||||
case 0: EXPECT_LE(norm, 13); break;
|
||||
default: ADD_FAILURE() << "Unknown quality setting: " << quality; break;
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Imgcodecs_AVIF, Imgcodecs_Avif_16bit,
|
||||
testing::Values(0, 50, 100) // Quality
|
||||
);
|
||||
#endif // OPENCV_IMGCODECS_AVIF_SUPPORT_16BIT
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
|
||||
|
||||
Reference in new issue
Block a user