Merge pull request #21966 from Harvey-Huang:Unicode_Path
authorHarvey Huang <619328684@qq.com>
Tue, 11 Oct 2022 11:25:35 +0000 (19:25 +0800)
committerGitHub <noreply@github.com>
Tue, 11 Oct 2022 11:25:35 +0000 (14:25 +0300)
Support use memory buffer to read multi-page image

modules/imgcodecs/include/opencv2/imgcodecs.hpp
modules/imgcodecs/src/loadsave.cpp
modules/imgcodecs/test/test_tiff.cpp

index 18b04d5..a95a4cc 100644 (file)
@@ -306,6 +306,20 @@ reallocations when the function is called repeatedly for images of the same size
 */
 CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst);
 
+/** @brief Reads a multi-page image from a buffer in memory.
+
+The function imdecodemulti reads a multi-page image from the specified buffer in the memory. If the buffer is too short or
+contains invalid data, the function returns false.
+
+See cv::imreadmulti for the list of supported formats and flags description.
+
+@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
+@param buf Input array or vector of bytes.
+@param flags The same flags as in cv::imread, see cv::ImreadModes.
+@param mats A vector of Mat objects holding each page, if more than one.
+*/
+CV_EXPORTS_W bool imdecodemulti(InputArray buf, int flags, CV_OUT std::vector<Mat>& mats);
+
 /** @brief Encodes an image into a memory buffer.
 
 The function imencode compresses the image and stores it in the memory buffer that is resized to fit the
index daad280..10c4dca 100644 (file)
@@ -929,6 +929,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 )
 {
index 76a39c0..dc8624d 100644 (file)
@@ -442,6 +442,43 @@ TEST_P(Imgcodecs_Tiff_Modes, decode_multipage)
     }
 }
 
+TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer)
+{
+    const int mode = GetParam();
+    const string root = cvtest::TS::ptr()->get_data_path();
+    const string filename = root + "readwrite/multipage.tif";
+    const string page_files[] = {
+        "readwrite/multipage_p1.tif",
+        "readwrite/multipage_p2.tif",
+        "readwrite/multipage_p3.tif",
+        "readwrite/multipage_p4.tif",
+        "readwrite/multipage_p5.tif",
+        "readwrite/multipage_p6.tif"
+    };
+    const size_t page_count = sizeof(page_files) / sizeof(page_files[0]);
+    vector<Mat> pages;
+
+    FILE* fp = fopen(filename.c_str(), "rb");
+    ASSERT_TRUE(fp != NULL);
+    fseek(fp, 0, SEEK_END);
+    long pos = ftell(fp);
+
+    std::vector<uchar> buf;
+    buf.resize((size_t)pos);
+    fseek(fp, 0, SEEK_SET);
+    buf.resize(fread(&buf[0], 1, buf.size(), fp));
+    fclose(fp);
+
+    bool res = imdecodemulti(buf, mode, pages);
+    ASSERT_TRUE(res == true);
+    ASSERT_EQ(page_count, pages.size());
+    for (size_t i = 0; i < page_count; i++)
+    {
+        const Mat page = imread(root + page_files[i], mode);
+        EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), page, pages[i]);
+    }
+}
+
 const int all_modes[] =
 {
     IMREAD_UNCHANGED,