[GSoC 2022] spng encoder/decoder added as optional png codec#22226
[GSoC 2022] spng encoder/decoder added as optional png codec#22226asmorkalov merged 3 commits intoopencv:4.xfrom
Conversation
27ab72d to
c204f95
Compare
modules/imgcodecs/src/grfmt_spng.cpp
Outdated
| if(!ret) { | ||
| image_width = image_size / m_height; | ||
|
|
||
| ret = spng_decode_image(png_ptr, nullptr, 0, fmt, SPNG_DECODE_PROGRESSIVE | decode_flags); |
There was a problem hiding this comment.
is it necessary to call this function? You decode the image row by row after it in any case.
There was a problem hiding this comment.
spng_decode_row() requires the decoder to be initialized by calling spng_decode_image() with the SPNG_DECODE_PROGRESSIVE flag set. This function call does not decode image. It just sets internal flags. It is necessary.
modules/imgcodecs/src/grfmt_spng.cpp
Outdated
| volatile bool result = false; | ||
| close(); | ||
|
|
||
| spng_ctx *ctx = spng_ctx_new(0); |
There was a problem hiding this comment.
The following lines should be
if(!ctx) {
spng_ctx_free(ctx);
return false;
}I missed that. Will fix it!
Done!
modules/imgcodecs/src/grfmt_spng.cpp
Outdated
| } | ||
| } | ||
|
|
||
| close(); |
There was a problem hiding this comment.
the API is unstable. You call close() after data is read. User may want, for some reason, call readData several times.
There was a problem hiding this comment.
I don't see a reason why a user wants to read the same data twice. The current png decoder is implemented in this way too. After reading the data, the user can just copy it.
Reference to the current PNG codec.
https://github.com/opencv/opencv/blob/4.x/modules/imgcodecs/src/grfmt_png.cpp#L307
Mat mat;
decoder.readData(mat)
Mat matOther;
matOther = mat;Users should favor that in case of performance. But I think you are right. If the user calls twice in a row, its behavior is undefined. We also should remove this from the current png codec implementation too.
There was a problem hiding this comment.
I changed it to
if(!result)
close();Should I create a quick patch for the current PNG decoder too?
modules/imgcodecs/src/grfmt_spng.cpp
Outdated
| } | ||
|
|
||
|
|
||
| int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length) |
There was a problem hiding this comment.
I do not see where proper encoding to memory is implemented. SPngEncoder::write() only writes image to disk
There was a problem hiding this comment.
If if understand it correctly, there are related tests in OpenCV for that. It encodes to the buffer and compares with some Vector. All the test cases is passed.
The answer to your question is in line 407, it checks if the encoder should write it to buffer or file. I copied that part from the libpng codec.
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 );
}Here spng_set_png_stream function call does that.
9456e7b to
5f15415
Compare
modules/imgcodecs/src/grfmt_spng.cpp
Outdated
| spng_ctx *ctx = spng_ctx_new(0); | ||
|
|
||
| if(!ctx) { | ||
| spng_ctx_free(ctx); |
There was a problem hiding this comment.
Please check if spng_ctx_free with NULL is expected behaviour.
There was a problem hiding this comment.
void spng_ctx_free(spng_ctx *ctx)
{
if(ctx == NULL) return;It is expected behavior but it seems unnecessary. For the future releases of spng, it might be useful to keep this line.
modules/imgcodecs/src/grfmt_spng.cpp
Outdated
| m_ctx = ctx; | ||
| spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); | ||
|
|
||
| size_t limit = 1024 * 1024 * 64; |
There was a problem hiding this comment.
Author of the libspng wrote this in the example for the setting the chunk limit.
/* Set memory usage limits for storing standard and unknown chunks,
this is important when reading untrusted files! */I thought it was a good idea. But this limits the chunks size. Now I think it is better if I remove it. This methods equivalent is png_set_chunk_malloc_max.
Should i keep this or remove it?
There was a problem hiding this comment.
I do not have strong opinion here. It's ok to have chunk limit. Please add note why you use the particular value or link to original code, if it's inherited behaviour.
|
I did some performance tests and right now it performs worse than the current png codec. I am going to look more at how I could improve it. Let's not merge this one yet. Edit: I managed to improve a little bit but i think because of my computer, test results are unreliable. The code: const string root = cvtest::TS::ptr()->get_data_path();
const string imgName = root + "../cv/shared/lena.png";
cv::TickMeter tick;
tick.start();
for(int i = 0; i< 1000; ++i) {
Mat original_image = imread(imgName);
}
tick.stop();
std::cout << tick.getAvgTimeMilli()/1000 << "\n";I run this 4 time for each codec. Here are the results:
Let me know what you think Decode PNG: [ PERFSTAT ] (samples=100 mean=8.30 median=5.18 min=4.93 stddev=5.25 (63.2%)) Encode PNG: [ PERFSTAT ] (samples=100 mean=22.84 median=22.66 min=12.51 stddev=5.07 (22.2%)) |
5c9a09b to
b3a36ec
Compare
|
SPNG perf: PNG perf: Well interesting. I upgraded my Ubuntu to 22.04 and it seems SPNG slightly performs better now. And i changed only one line. Edit: About ignoring ADLER32 checksum, there is another library that uses libspng, libvips, also does the same thing. And since deflate streams lossless compressions, there is no need to check checksum in deflate streams. |
|
Potential performance improvement: Convert RGB to BGR while decoding image row-by-row. All test cases are passed but ROI still might not work. ret = spng_decode_row(png_ptr, img.data + row_info.row_num * image_width, image_width);Add img.step here. |
|
imread(img, IMREAD_GRAYSCALE) tests fail. I think the reason is png RGB->Gray conversion is different than OpenCV RGB->Gray. Edit: Note to myself, change project name SPNG_LIBRARY to PNG_LIBRARY |
|
@asmorkalov can you run the workflow? Lets see if spng works on other platforms. |
|
With the 3024x4032 size and 11mb image, the perf results are: [ RUN ] PNG.decode
[ PERFSTAT ] (samples=10 mean=179.53 median=178.60 min=177.07 stddev=2.56 (1.4%))
cmake release mode spng
[ RUN ] PNG.decode
[ PERFSTAT ] (samples=10 mean=151.74 median=150.96 min=150.39 stddev=1.60 (1.1%)) |
|
@ocpalo, I tested it on my machine; works well well 👍 |
|
I temporarily changed cmake changes to test spng decoder in CI. Before merge, let me revert cmake changes and make spng optional decoder. |
|
Ubuntu2004-x64 - macOS-ARM/x64 Error: [ RUN ] Test_ONNX_layers.ReduceSum/0, where GetParam() = OCV/CPU
unknown file: Failure
C++ exception with description "OpenCV(4.6.0-dev) /home/ci/opencv/modules/ts/src/ts.cpp:1064: error: (-2:Unspecified error) OpenCV tests: Can't find required data file: dnn/onnx/models/reduce_sum_axis_dynamic_batch.onnx in function 'findData'
" thrown in the test body.Windows Error: I need to figure out how to compile libspng in Windows environment |
|
reminder: currently we see in windows |
|
I rebased all of my commits with the changes that alalek requested on CMakeLists. I am working on to update tests for pngsuite. I will probably finish by the noon, tomorrow. |
|
Few linux commands did the trick. Here we go. Thanks to @sturkmen72 for separate xml files. He was generated those files while ago and shared with me. @alalek @vpisarev could you review if this is ok now? |
|
@alalek I have updated the test_png files. But as i said above, i did not understand how to handle zlib dependency handling if ZLIB is disabled. If you provide me more information, i am happy to do it :) |
|
fyi, i force pushed to fix docs warnings(whitespaces) |
|
It seems the runflow fails on some flows. However, when i check the build logs, it builds with the libpng since i have made it libspng as optional. I think the workflow fails are not related with my pullrequest. edit: i made spng as optional so it does not build during the workflow. |
|
You can check this workflow where i enabled libspng. Since then, i have not made any changes in the code except alalek cmake and test requests. @asmorkalov . |
|
@asmorkalov, looks like the failures have no relation with the patch. Can we finally merge it? |
|
I tested the PR manually with ARMv7 with Ubuntu 14.04 and x86_64 with Ubuntu 18.04.
|
|
@asmorkalov that is a very good catch. I was not able to catch this because I have not tested after changing unit test structure. I assumed it will pass as before but it seems alalek was right to change to catch this kind of issues. About the issue, in the implementation, I ignore the checksum of IHDR and IDAT. I prefer to do it that way because many libraries ignores it in their implementation. Since it does not calculate the checksum, it decodes the images into mat object. How should I approach this situation? Ignore the calculation of checksum and change unit tests or should I modify the existing code to calculate the checksum? I think it is better you, opencv team, decide how to handle this situation. Downside of calculation checksum is it reduces the performance. |
|
It's interesting question. I do not see issue with Ubuntu 14.04 on my jetson tk1. First of all we need deterministic behavior everywhere. I'm looking on Ubuntu 20.04 right now to find correlation. |
|
for information see http://www.schaik.com/pngsuite/pngsuite_xxx_png.html |
|
The issue is reproducible with Ubuntu 20.04. |
|
@asmorkalov to keep the existing behaviour of the current png codec, i have changed to calculate to checksum. Sorry to bother you with this. I have tested on Ubuntu 22.04 and tests are passed now. Could you please review/re-run your tests? As a side note, we can provide some kind of flags to users to set these. For example, in my latest pull request about new imread function with named-parameters, we can add some kind of flags, boolean, enum etc, to let user to control it. I am not expert at codecs but i believe most of the codecs have this kind of checks, flags etc. |
|
@asmorkalov, can you check this patch once again and see if the problem is solved on your Ubuntu machine? |
asmorkalov
left a comment
There was a problem hiding this comment.
👍 Great job! Thanks a lot for the contribution!
opencv#22226 libspng, pngsuite and png image for perf test


Merge with extra: opencv/opencv_extra#995
This pull request contains the implementation of PngEncoder using spng library. One thing that I am not sure about is the CMake configuration. I appreciate any help on CMake configuration
Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
Patch to opencv_extra has the same branch name.