Merge pull request #30685 from sshivensolanki:core-filestorage-unsigned

core: keep large CV_32U values when reading with FileStorage - #30685

Fixes #30684

### Problem

When a `CV_32U` `Mat` was loaded with `FileStorage`, values above `INT_MAX` came back as `0`, in YAML, XML and JSON, with and without base64. The saved file had the correct values, so only reading was affected.

### Fix

- `FileNodeIterator::readRaw` read elements of up to 4 bytes through `(int)node`, so these values turned negative and the `CV_32U` case clamped them to 0. It now always reads the node as `int64_t`. Each case still converts the value to its own type.
- The base64 path stored `getInt32()` as signed, so `CV_32U` now uses a new `Base64Decoder::getUInt32()`, like `getUInt8()` and `getUInt16()`.

`CV_64U` values above `INT64_MAX` are also lost in the text formats, but that has a different cause, in the parsers' `strtoll`, so it is not part of this PR.

### Tests

New `Core_InputOutput.filestorage_32u_large_values` saves values on both sides of `INT_MAX` and loads them back, in YAML, XML and JSON, with and without base64. All 6 combinations fail without the fix, and the test passes with it on Apple M1 Pro.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Shiven Solanki
2026-11-05 19:17:21 +08:00
committed by GitHub
parent 3b42da9cda
commit 545147fdab
3 changed files with 32 additions and 2 deletions

View File

@@ -1837,6 +1837,10 @@ ushort FileStorage::Impl::Base64Decoder::getUInt16() {
return val;
}
unsigned FileStorage::Impl::Base64Decoder::getUInt32() {
return (unsigned)getInt32();
}
int FileStorage::Impl::Base64Decoder::getInt32() {
size_t sz = decoded.size();
if (ofs + 4 > sz && !readMore(4))
@@ -1914,7 +1918,7 @@ char *FileStorage::Impl::parseBase64(char *ptr, int indent, FileNode &collection
ival = (short) base64decoder.getUInt16();
break;
case CV_32U:
ival = base64decoder.getInt32();
ival = base64decoder.getUInt32();
break;
case CV_32S:
ival = base64decoder.getInt32();
@@ -2743,7 +2747,7 @@ FileNodeIterator& FileNodeIterator::readRaw( const String& fmt, void* _data0, si
FileNode node = *(*this);
if( node.isInt() )
{
int64_t ival = static_cast<int64_t>(elem_size == 8 ? (int64_t)node : (int)node);
int64_t ival = (int64_t)node;
switch( elem_type )
{
case CV_8U:

View File

@@ -151,6 +151,8 @@ public:
ushort getUInt16();
unsigned getUInt32();
int getInt32();
int64_t getInt64();

View File

@@ -2662,4 +2662,28 @@ TEST(Core_InputOutput, filestorage_read_uppercase_exponent)
}
}
// CV_32U values above INT_MAX must survive saving and loading, in every format
TEST(Core_InputOutput, filestorage_32u_large_values)
{
const unsigned values[] = { 0u, 1u, 2147483647u, 2147483648u, 4294967295u };
const Mat src(1, 5, CV_32U, (void*)values);
for (const char* ext : { ".yml", ".xml", ".json" })
{
for (int base64 : { 0, (int)FileStorage::WRITE_BASE64 })
{
FileStorage writer(ext, FileStorage::WRITE | FileStorage::MEMORY | base64);
writer << "m" << src;
const std::string text = writer.releaseAndGetString();
FileStorage reader(text, FileStorage::READ | FileStorage::MEMORY);
Mat dst;
reader["m"] >> dst;
EXPECT_EQ(CV_32U, dst.type()) << ext << " base64=" << base64;
EXPECT_EQ(0, cv::norm(src, dst, NORM_INF)) << ext << " base64=" << base64;
}
}
}
}} // namespace