test_avif.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level
  3. // directory of this distribution and at http://opencv.org/license.html
  4. #include <cstdint>
  5. #include <fstream>
  6. #include "test_precomp.hpp"
  7. #ifdef HAVE_AVIF
  8. namespace opencv_test {
  9. namespace {
  10. class Imgcodecs_Avif_RoundTripSuite
  11. : public testing::TestWithParam<std::tuple<int, int, int, ImreadModes>> {
  12. protected:
  13. static cv::Mat modifyImage(const cv::Mat& img_original, int channels,
  14. int bit_depth) {
  15. cv::Mat img;
  16. if (channels == 1) {
  17. cv::cvtColor(img_original, img, cv::COLOR_BGR2GRAY);
  18. } else if (channels == 4) {
  19. std::vector<cv::Mat> imgs;
  20. cv::split(img_original, imgs);
  21. imgs.push_back(cv::Mat(imgs[0]));
  22. imgs[imgs.size() - 1] = cv::Scalar::all(128);
  23. cv::merge(imgs, img);
  24. } else {
  25. img = img_original.clone();
  26. }
  27. cv::Mat img_final = img;
  28. // Convert image to CV_16U for some bit depths.
  29. if (bit_depth > 8) img.convertTo(img_final, CV_16U, 1 << (bit_depth - 8));
  30. return img_final;
  31. }
  32. void SetUp() {
  33. bit_depth_ = std::get<0>(GetParam());
  34. channels_ = std::get<1>(GetParam());
  35. quality_ = std::get<2>(GetParam());
  36. imread_mode_ = std::get<3>(GetParam());
  37. encoding_params_ = {cv::IMWRITE_AVIF_QUALITY, quality_,
  38. cv::IMWRITE_AVIF_DEPTH, bit_depth_};
  39. }
  40. bool IsBitDepthValid() const {
  41. return (bit_depth_ == 8 || bit_depth_ == 10 || bit_depth_ == 12);
  42. }
  43. // Makes sure images are close enough after encode/decode roundtrip.
  44. void ValidateRead(const cv::Mat& img_original, const cv::Mat& img) const {
  45. EXPECT_EQ(img_original.size(), img.size());
  46. if (imread_mode_ == IMREAD_UNCHANGED) {
  47. ASSERT_EQ(img_original.type(), img.type());
  48. // Lossless.
  49. if (quality_ == 100) {
  50. EXPECT_EQ(0, cvtest::norm(img, img_original, NORM_INF));
  51. } else {
  52. const float norm = cvtest::norm(img, img_original, NORM_L2) /
  53. img.channels() / img.cols / img.rows /
  54. (1 << (bit_depth_ - 8));
  55. if (quality_ == 50) {
  56. EXPECT_LE(norm, 10);
  57. } else if (quality_ == 0) {
  58. EXPECT_LE(norm, 13);
  59. } else {
  60. EXPECT_FALSE(true);
  61. }
  62. }
  63. }
  64. }
  65. public:
  66. int bit_depth_;
  67. int channels_;
  68. int quality_;
  69. int imread_mode_;
  70. std::vector<int> encoding_params_;
  71. };
  72. ////////////////////////////////////////////////////////////////////////////////
  73. class Imgcodecs_Avif_Image_RoundTripSuite
  74. : public Imgcodecs_Avif_RoundTripSuite {
  75. public:
  76. const cv::Mat& get_img_original() {
  77. const Key key = {channels_, (bit_depth_ < 8) ? 8 : bit_depth_};
  78. return imgs_[key];
  79. }
  80. // Prepare the original image modified for different number of channels and
  81. // bit depth.
  82. static void SetUpTestCase() {
  83. const string root = cvtest::TS::ptr()->get_data_path();
  84. const string filename = root + "../cv/shared/lena.png";
  85. const cv::Mat img_original = cv::imread(filename);
  86. cv::Mat img_resized;
  87. cv::resize(img_original, img_resized, cv::Size(kWidth, kHeight), 0, 0);
  88. for (int channels : {1, 3, 4}) {
  89. for (int bit_depth : {8, 10, 12}) {
  90. const Key key{channels, bit_depth};
  91. imgs_[key] = modifyImage(img_resized, channels, bit_depth);
  92. }
  93. }
  94. }
  95. static const int kWidth;
  96. static const int kHeight;
  97. private:
  98. typedef std::tuple<int, int> Key;
  99. static std::map<Key, cv::Mat> imgs_;
  100. };
  101. std::map<std::tuple<int, int>, cv::Mat>
  102. Imgcodecs_Avif_Image_RoundTripSuite::imgs_;
  103. const int Imgcodecs_Avif_Image_RoundTripSuite::kWidth = 51;
  104. const int Imgcodecs_Avif_Image_RoundTripSuite::kHeight = 31;
  105. class Imgcodecs_Avif_Image_WriteReadSuite
  106. : public Imgcodecs_Avif_Image_RoundTripSuite {};
  107. TEST_P(Imgcodecs_Avif_Image_WriteReadSuite, imwrite_imread) {
  108. const cv::Mat& img_original = get_img_original();
  109. ASSERT_FALSE(img_original.empty());
  110. // Encode.
  111. const string output = cv::tempfile(".avif");
  112. EXPECT_NO_THROW(cv::imwrite(output, img_original, encoding_params_));
  113. // Read from file.
  114. const cv::Mat img = cv::imread(output, imread_mode_);
  115. ValidateRead(img_original, img);
  116. EXPECT_EQ(0, remove(output.c_str()));
  117. }
  118. INSTANTIATE_TEST_CASE_P(
  119. Imgcodecs_AVIF, Imgcodecs_Avif_Image_WriteReadSuite,
  120. ::testing::Combine(::testing::ValuesIn({6, 8, 10, 12}),
  121. ::testing::ValuesIn({1, 3, 4}),
  122. ::testing::ValuesIn({0, 50, 100}),
  123. ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
  124. IMREAD_COLOR, IMREAD_COLOR_RGB})));
  125. class Imgcodecs_Avif_Image_EncodeDecodeSuite
  126. : public Imgcodecs_Avif_Image_RoundTripSuite {};
  127. TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) {
  128. const cv::Mat& img_original = get_img_original();
  129. ASSERT_FALSE(img_original.empty());
  130. // Encode.
  131. std::vector<unsigned char> buf;
  132. bool result = true;
  133. EXPECT_NO_THROW(
  134. result = cv::imencode(".avif", img_original, buf, encoding_params_););
  135. EXPECT_TRUE(result);
  136. // Read back.
  137. const cv::Mat img = cv::imdecode(buf, imread_mode_);
  138. ValidateRead(img_original, img);
  139. }
  140. INSTANTIATE_TEST_CASE_P(
  141. Imgcodecs_AVIF, Imgcodecs_Avif_Image_EncodeDecodeSuite,
  142. ::testing::Combine(::testing::ValuesIn({6, 8, 10, 12}),
  143. ::testing::ValuesIn({1, 3, 4}),
  144. ::testing::ValuesIn({0, 50, 100}),
  145. ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
  146. IMREAD_COLOR, IMREAD_COLOR_RGB})));
  147. ////////////////////////////////////////////////////////////////////////////////
  148. class Imgcodecs_Avif_Animation_RoundTripSuite
  149. : public Imgcodecs_Avif_RoundTripSuite {
  150. public:
  151. const std::vector<cv::Mat>& get_anim_original() {
  152. const Key key = {channels_, bit_depth_};
  153. return anims_[key];
  154. }
  155. // Prepare the original image modified for different number of channels and
  156. // bit depth.
  157. static void SetUpTestCase() {
  158. const string root = cvtest::TS::ptr()->get_data_path();
  159. const string filename = root + "../cv/shared/lena.png";
  160. const cv::Mat img_original = cv::imread(filename);
  161. cv::Mat img_resized;
  162. cv::resize(img_original, img_resized, cv::Size(kWidth, kHeight), 0, 0);
  163. for (int channels : {1, 3, 4}) {
  164. for (int bit_depth : {8, 10, 12}) {
  165. const Key key{channels, bit_depth};
  166. const cv::Mat img = modifyImage(img_resized, channels, bit_depth);
  167. cv::Mat img2, img3;
  168. cv::flip(img, img2, 0);
  169. cv::flip(img, img3, -1);
  170. anims_[key] = {img, img2, img3};
  171. }
  172. }
  173. }
  174. void ValidateRead(const std::vector<cv::Mat>& anim_original,
  175. const std::vector<cv::Mat>& anim) const {
  176. ASSERT_EQ(anim_original.size(), anim.size());
  177. for (size_t i = 0; i < anim.size(); ++i) {
  178. Imgcodecs_Avif_RoundTripSuite::ValidateRead(anim_original[i], anim[i]);
  179. }
  180. }
  181. static const int kWidth;
  182. static const int kHeight;
  183. private:
  184. typedef std::tuple<int, int> Key;
  185. static std::map<Key, std::vector<cv::Mat>> anims_;
  186. };
  187. std::map<std::tuple<int, int>, std::vector<cv::Mat>>
  188. Imgcodecs_Avif_Animation_RoundTripSuite::anims_;
  189. const int Imgcodecs_Avif_Animation_RoundTripSuite::kWidth = 5;
  190. const int Imgcodecs_Avif_Animation_RoundTripSuite::kHeight = 5;
  191. class Imgcodecs_Avif_Animation_WriteReadSuite
  192. : public Imgcodecs_Avif_Animation_RoundTripSuite {};
  193. TEST_P(Imgcodecs_Avif_Animation_WriteReadSuite, encode_decode) {
  194. const std::vector<cv::Mat>& anim_original = get_anim_original();
  195. ASSERT_FALSE(anim_original.empty());
  196. // Encode.
  197. const string output = cv::tempfile(".avif");
  198. if (!IsBitDepthValid()) {
  199. EXPECT_THROW(cv::imwritemulti(output, anim_original, encoding_params_),
  200. cv::Exception);
  201. EXPECT_NE(0, remove(output.c_str()));
  202. return;
  203. }
  204. EXPECT_NO_THROW(cv::imwritemulti(output, anim_original, encoding_params_));
  205. EXPECT_EQ(anim_original.size(), imcount(output));
  206. // Read from file.
  207. std::vector<cv::Mat> anim;
  208. ASSERT_TRUE(cv::imreadmulti(output, anim, imread_mode_));
  209. ValidateRead(anim_original, anim);
  210. EXPECT_EQ(0, remove(output.c_str()));
  211. }
  212. INSTANTIATE_TEST_CASE_P(
  213. Imgcodecs_AVIF, Imgcodecs_Avif_Animation_WriteReadSuite,
  214. ::testing::Combine(::testing::ValuesIn({8, 10, 12}),
  215. ::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}),
  216. ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
  217. IMREAD_COLOR, IMREAD_COLOR_RGB})));
  218. class Imgcodecs_Avif_Animation_WriteDecodeSuite
  219. : public Imgcodecs_Avif_Animation_RoundTripSuite {};
  220. TEST_P(Imgcodecs_Avif_Animation_WriteDecodeSuite, encode_decode) {
  221. const std::vector<cv::Mat>& anim_original = get_anim_original();
  222. ASSERT_FALSE(anim_original.empty());
  223. // Encode.
  224. const string output = cv::tempfile(".avif");
  225. if (!IsBitDepthValid()) {
  226. EXPECT_THROW(cv::imwritemulti(output, anim_original, encoding_params_),
  227. cv::Exception);
  228. EXPECT_NE(0, remove(output.c_str()));
  229. return;
  230. }
  231. EXPECT_NO_THROW(cv::imwritemulti(output, anim_original, encoding_params_));
  232. // Put file into buffer and read from buffer.
  233. std::ifstream file(output, std::ios::binary | std::ios::ate);
  234. std::streamsize size = file.tellg();
  235. file.seekg(0, std::ios::beg);
  236. std::vector<unsigned char> buf(size);
  237. EXPECT_TRUE(file.read(reinterpret_cast<char*>(buf.data()), size));
  238. file.close();
  239. std::vector<cv::Mat> anim;
  240. ASSERT_TRUE(cv::imdecodemulti(buf, imread_mode_, anim));
  241. ValidateRead(anim_original, anim);
  242. if (imread_mode_ == IMREAD_UNCHANGED) {
  243. ImageCollection collection(output, IMREAD_UNCHANGED);
  244. anim.clear();
  245. for (auto&& i : collection)
  246. anim.push_back(i);
  247. ValidateRead(anim_original, anim);
  248. }
  249. EXPECT_EQ(0, remove(output.c_str()));
  250. }
  251. INSTANTIATE_TEST_CASE_P(
  252. Imgcodecs_AVIF, Imgcodecs_Avif_Animation_WriteDecodeSuite,
  253. ::testing::Combine(::testing::ValuesIn({8, 10, 12}),
  254. ::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}),
  255. ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
  256. IMREAD_COLOR, IMREAD_COLOR_RGB})));
  257. } // namespace
  258. } // namespace opencv_test
  259. #endif // HAVE_AVIF