diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/asset.h | 2 | ||||
| -rw-r--r-- | src/dcp.cc | 59 | ||||
| -rw-r--r-- | src/dcp.h | 8 | ||||
| -rw-r--r-- | src/mxf_asset.cc | 39 | ||||
| -rw-r--r-- | src/mxf_asset.h | 2 | ||||
| -rw-r--r-- | src/picture_asset.cc | 209 | ||||
| -rw-r--r-- | src/picture_asset.h | 11 | ||||
| -rw-r--r-- | src/reel.cc | 32 | ||||
| -rw-r--r-- | src/reel.h | 2 | ||||
| -rw-r--r-- | src/sound_asset.cc | 17 | ||||
| -rw-r--r-- | src/sound_asset.h | 2 | ||||
| -rw-r--r-- | src/subtitle_asset.h | 5 | ||||
| -rw-r--r-- | src/types.h | 13 |
13 files changed, 203 insertions, 198 deletions
diff --git a/src/asset.h b/src/asset.h index eab24d28..32fd93ae 100644 --- a/src/asset.h +++ b/src/asset.h @@ -68,7 +68,7 @@ public: return _uuid; } - virtual std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const = 0; + virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const = 0; protected: friend class PictureAsset; @@ -245,26 +245,26 @@ DCP::read (bool require_mxfs) } -list<string> -DCP::equals (DCP const & other, EqualityOptions opt) const +bool +DCP::equals (DCP const & other, EqualityOptions opt, list<string>& notes) const { - list<string> notes; - if (_cpls.size() != other._cpls.size()) { notes.push_back ("CPL counts differ"); + return false; } list<shared_ptr<const CPL> >::const_iterator a = _cpls.begin (); list<shared_ptr<const CPL> >::const_iterator b = other._cpls.begin (); while (a != _cpls.end ()) { - list<string> n = (*a)->equals (*b->get(), opt); - notes.merge (n); + if (!(*a)->equals (*b->get(), opt, notes)) { + return false; + } ++a; ++b; } - return notes; + return true; } @@ -509,39 +509,44 @@ CPL::write_to_assetmap (ostream& s) const -list<string> -CPL::equals (CPL const & other, EqualityOptions opt) const +bool +CPL::equals (CPL const & other, EqualityOptions opt, list<string>& notes) const { - list<string> notes; - - if (opt.flags & LIBDCP_METADATA) { - if (_name != other._name) { - notes.push_back ("names differ"); - } - if (_content_kind != other._content_kind) { - notes.push_back ("content kinds differ"); - } - if (_fps != other._fps) { - notes.push_back ("frames per second differ"); - } - if (_length != other._length) { - notes.push_back ("lengths differ"); - } + if (_name != other._name) { + notes.push_back ("names differ"); + return false; + } + + if (_content_kind != other._content_kind) { + notes.push_back ("content kinds differ"); + return false; + } + + if (_fps != other._fps) { + notes.push_back ("frames per second differ"); + return false; + } + + if (_length != other._length) { + notes.push_back ("lengths differ"); + return false; } if (_reels.size() != other._reels.size()) { notes.push_back ("reel counts differ"); + return false; } list<shared_ptr<const Reel> >::const_iterator a = _reels.begin (); list<shared_ptr<const Reel> >::const_iterator b = other._reels.begin (); while (a != _reels.end ()) { - list<string> n = (*a)->equals (*b, opt); - notes.merge (n); + if (!(*a)->equals (*b, opt, notes)) { + return false; + } ++a; ++b; } - return notes; + return true; } @@ -83,7 +83,7 @@ public: std::list<boost::shared_ptr<const Asset> > assets () const; - std::list<std::string> equals (CPL const & other, EqualityOptions options) const; + bool equals (CPL const & other, EqualityOptions options, std::list<std::string>& notes) const; void write_xml () const; void write_to_assetmap (std::ostream& s) const; @@ -139,10 +139,10 @@ public: /** Compare this DCP with another, according to various options. * @param other DCP to compare this one to. * @param options Options to define just what "equality" means. - * @return An empty list if the DCPs are equal; otherwise a list of messages - * which explain the ways in which they differ. + * @param notes Filled in with notes about differences. + * @return true if the DCPs are equal according to EqualityOptions, otherwise false. */ - std::list<std::string> equals (DCP const & other, EqualityOptions options) const; + bool equals (DCP const & other, EqualityOptions options, std::list<std::string>& notes) const; void add_cpl (boost::shared_ptr<CPL> cpl); diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc index 2246c55d..d0e02c8b 100644 --- a/src/mxf_asset.cc +++ b/src/mxf_asset.cc @@ -57,37 +57,40 @@ MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info) const assert (c == Kumu::UUID_Length); } -list<string> -MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const +bool +MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const { shared_ptr<const MXFAsset> other_mxf = dynamic_pointer_cast<const MXFAsset> (other); if (!other_mxf) { - return list<string> (); + notes.push_back ("comparing an MXF asset with a non-MXF asset"); + return false; } - list<string> notes; + if (_file_name != other_mxf->_file_name) { + notes.push_back ("MXF names differ"); + return false; + } + + if (_fps != other_mxf->_fps) { + notes.push_back ("MXF frames per second differ"); + return false; + } - if (opt.flags & LIBDCP_METADATA) { - if (_file_name != other_mxf->_file_name) { - notes.push_back ("MXF names differ"); - } - if (_fps != other_mxf->_fps) { - notes.push_back ("MXF frames per second differ"); - } - if (_length != other_mxf->_length) { - notes.push_back ("MXF lengths differ"); - } + if (_length != other_mxf->_length) { + notes.push_back ("MXF lengths differ"); + return false; } - if (opt.flags & MXF_BITWISE) { + if (opt.bitwise) { if (digest() != other_mxf->digest()) { notes.push_back ("MXF digests differ"); + return false; } if (filesystem::file_size (path()) != filesystem::file_size (other_mxf->path())) { notes.push_back (path().string() + " and " + other_mxf->path().string() + " sizes differ"); - return notes; + return false; } ifstream a (path().string().c_str(), ios::binary); @@ -106,14 +109,14 @@ MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const if (memcmp (abuffer, bbuffer, t) != 0) { notes.push_back (path().string() + " and " + other_mxf->path().string() + " content differs"); - return notes; + return false; } n -= t; } } - return notes; + return true; } int diff --git a/src/mxf_asset.h b/src/mxf_asset.h index 7f36b30a..240e042d 100644 --- a/src/mxf_asset.h +++ b/src/mxf_asset.h @@ -38,7 +38,7 @@ public: */ MXFAsset (std::string directory, std::string file_name, sigc::signal1<void, float>* progress, int fps, int entry_point, int length); - virtual std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const; + virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const; int length () const; diff --git a/src/picture_asset.cc b/src/picture_asset.cc index a1d8321d..898fe75b 100644 --- a/src/picture_asset.cc +++ b/src/picture_asset.cc @@ -63,12 +63,14 @@ PictureAsset::write_to_cpl (ostream& s) const << " </MainPicture>\n"; } -list<string> -PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const +bool +PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const { - list<string> notes = MXFAsset::equals (other, opt); + if (!MXFAsset::equals (other, opt, notes)) { + return false; + } - if (opt.flags & MXF_INSPECT) { + if (!opt.bitwise) { ASDCP::JP2K::MXFReader reader_A; if (ASDCP_FAILURE (reader_A.OpenRead (path().string().c_str()))) { throw MXFFileError ("could not open MXF file for reading", path().string()); @@ -119,7 +121,7 @@ PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const // } } - return notes; + return true; } @@ -229,139 +231,134 @@ MonoPictureAsset::get_frame (int n) const } -list<string> -MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const +bool +MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const { - list<string> notes = PictureAsset::equals (other, opt); + if (!PictureAsset::equals (other, opt, notes)) { + return false; + } shared_ptr<const MonoPictureAsset> other_picture = dynamic_pointer_cast<const MonoPictureAsset> (other); assert (other_picture); - for (int i = 0; i < _length; ++i) { - shared_ptr<const MonoPictureFrame> frame_A = get_frame (i); - shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i); - - list<string> n = frame_buffer_equals ( - i, opt, - frame_A->j2k_frame()->RoData(), frame_A->j2k_frame()->Size(), - frame_B->j2k_frame()->RoData(), frame_B->j2k_frame()->Size() - ); - - notes.merge (n); + if (!opt.bitwise) { + for (int i = 0; i < _length; ++i) { + shared_ptr<const MonoPictureFrame> frame_A = get_frame (i); + shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i); + + if (!frame_buffer_equals ( + i, opt, notes, + frame_A->j2k_frame()->RoData(), frame_A->j2k_frame()->Size(), + frame_B->j2k_frame()->RoData(), frame_B->j2k_frame()->Size() + )) { + return false; + } + } } - return notes; + return true; } -list<string> -StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const +bool +StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const { - list<string> notes = PictureAsset::equals (other, opt); + if (!PictureAsset::equals (other, opt, notes)) { + return false; + } shared_ptr<const StereoPictureAsset> other_picture = dynamic_pointer_cast<const StereoPictureAsset> (other); assert (other_picture); - - for (int i = 0; i < _length; ++i) { - shared_ptr<const StereoPictureFrame> frame_A = get_frame (i); - shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i); - - list<string> n = frame_buffer_equals ( - i, opt, - frame_A->j2k_frame()->Left.RoData(), frame_A->j2k_frame()->Left.Size(), - frame_B->j2k_frame()->Left.RoData(), frame_B->j2k_frame()->Left.Size() - ); - - notes.merge (n); - n = frame_buffer_equals ( - i, opt, - frame_A->j2k_frame()->Right.RoData(), frame_A->j2k_frame()->Right.Size(), - frame_B->j2k_frame()->Right.RoData(), frame_B->j2k_frame()->Right.Size() - ); + if (!opt.bitwise) { + + for (int i = 0; i < _length; ++i) { + shared_ptr<const StereoPictureFrame> frame_A = get_frame (i); + shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i); + + if (!frame_buffer_equals ( + i, opt, notes, + frame_A->j2k_frame()->Left.RoData(), frame_A->j2k_frame()->Left.Size(), + frame_B->j2k_frame()->Left.RoData(), frame_B->j2k_frame()->Left.Size() + )) { + return false; + } - notes.merge (n); + if (!frame_buffer_equals ( + i, opt, notes, + frame_A->j2k_frame()->Right.RoData(), frame_A->j2k_frame()->Right.Size(), + frame_B->j2k_frame()->Right.RoData(), frame_B->j2k_frame()->Right.Size() + )) { + return false; + } + } } - return notes; + return true; } -list<string> +bool PictureAsset::frame_buffer_equals ( - int frame, EqualityOptions opt, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B + int frame, EqualityOptions opt, list<string>& notes, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B ) const { - list<string> notes; + if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) { + /* Easy result; the J2K data is identical */ + return true; + } + + /* Decompress the images to bitmaps */ + opj_image_t* image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0); + opj_image_t* image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0); + + /* Compare them */ - bool j2k_same = true; - if (size_A != size_B) { - notes.push_back ("sizes of video data for frame " + lexical_cast<string>(frame) + " differ"); - j2k_same = false; - } else if (memcmp (data_A, data_B, size_A) != 0) { - notes.push_back ("J2K data for frame " + lexical_cast<string>(frame) + " differ"); - j2k_same = false; + if (image_A->numcomps != image_B->numcomps) { + notes.push_back ("image component counts for frame " + lexical_cast<string>(frame) + " differ"); + return false; } - if (!j2k_same) { - - if (opt.verbose) { - cout << "J2K images for " << frame << " differ; checking by pixel\n"; - } - - /* Decompress the images to bitmaps */ - opj_image_t* image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0); - opj_image_t* image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0); - - /* Compare them */ - - if (image_A->numcomps != image_B->numcomps) { - notes.push_back ("image component counts for frame " + lexical_cast<string>(frame) + " differ"); - } - - vector<int> abs_diffs (image_A->comps[0].w * image_A->comps[0].h * image_A->numcomps); - int d = 0; - int max_diff = 0; - - for (int c = 0; c < image_A->numcomps; ++c) { - - if (image_A->comps[c].w != image_B->comps[c].w || image_A->comps[c].h != image_B->comps[c].h) { - notes.push_back ("image sizes for frame " + lexical_cast<string>(frame) + " differ"); - } - - int const pixels = image_A->comps[c].w * image_A->comps[c].h; - for (int j = 0; j < pixels; ++j) { - int const t = abs (image_A->comps[c].data[j] - image_B->comps[c].data[j]); - abs_diffs[d++] = t; - max_diff = max (max_diff, t); - } - } - - uint64_t total = 0; - for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { - total += *j; - } - - double const mean = double (total) / abs_diffs.size (); - - uint64_t total_squared_deviation = 0; - for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { - total_squared_deviation += pow (*j - mean, 2); - } - - double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); + vector<int> abs_diffs (image_A->comps[0].w * image_A->comps[0].h * image_A->numcomps); + int d = 0; + int max_diff = 0; + + for (int c = 0; c < image_A->numcomps; ++c) { - if (mean > opt.max_mean_pixel_error || std_dev > opt.max_std_dev_pixel_error) { - notes.push_back ("mean or standard deviation out of range for " + lexical_cast<string>(frame)); + if (image_A->comps[c].w != image_B->comps[c].w || image_A->comps[c].h != image_B->comps[c].h) { + notes.push_back ("image sizes for frame " + lexical_cast<string>(frame) + " differ"); + return false; } - if (opt.verbose) { - cout << "\tmax pixel error " << max_diff << ", mean pixel error " << mean << ", standard deviation " << std_dev << "\n"; + int const pixels = image_A->comps[c].w * image_A->comps[c].h; + for (int j = 0; j < pixels; ++j) { + int const t = abs (image_A->comps[c].data[j] - image_B->comps[c].data[j]); + abs_diffs[d++] = t; + max_diff = max (max_diff, t); } + } - opj_image_destroy (image_A); - opj_image_destroy (image_B); + uint64_t total = 0; + for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { + total += *j; + } + + double const mean = double (total) / abs_diffs.size (); + + uint64_t total_squared_deviation = 0; + for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { + total_squared_deviation += pow (*j - mean, 2); + } + + double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); + + if (mean > opt.max_mean_pixel_error || std_dev > opt.max_std_dev_pixel_error) { + notes.push_back ("mean or standard deviation out of range for " + lexical_cast<string>(frame)); + return false; } + + opj_image_destroy (image_A); + opj_image_destroy (image_B); - return notes; + return true; } diff --git a/src/picture_asset.h b/src/picture_asset.h index baea22c3..da3eb7d0 100644 --- a/src/picture_asset.h +++ b/src/picture_asset.h @@ -41,7 +41,7 @@ public: */ void write_to_cpl (std::ostream& s) const; - std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const; + bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const; int width () const { return _width; @@ -53,8 +53,9 @@ public: protected: - std::list<std::string> frame_buffer_equals ( - int frame, EqualityOptions opt, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B + bool frame_buffer_equals ( + int frame, EqualityOptions opt, std::list<std::string>& notes, + uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B ) const; /** picture width in pixels */ @@ -114,7 +115,7 @@ public: MonoPictureAsset (std::string directory, std::string mxf_name, int fps, int entry_point, int length); boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const; - std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const; + bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const; private: std::string path_from_list (int f, std::vector<std::string> const & files) const; @@ -128,7 +129,7 @@ public: StereoPictureAsset (std::string directory, std::string mxf_name, int fps, int entry_point, int length); boost::shared_ptr<const StereoPictureFrame> get_frame (int n) const; - std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const; + bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const; }; diff --git a/src/reel.cc b/src/reel.cc index 50426cc1..52a4f0fb 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -46,38 +46,36 @@ Reel::write_to_cpl (ostream& s) const } } -list<string> -Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt) const +bool +Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, list<string>& notes) const { - list<string> o; - if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) { - o.push_back ("reel has different assets"); + notes.push_back ("reel has different assets"); + return false; } - if (_main_picture) { - list<string> m = _main_picture->equals (other->_main_picture, opt); - o.merge (m); + if (_main_picture && !_main_picture->equals (other->_main_picture, opt, notes)) { + return false; } if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) { - o.push_back ("reel has different assets"); + notes.push_back ("reel has different assets"); + return false; } - if (_main_sound) { - list<string> m = _main_sound->equals (other->_main_sound, opt); - o.merge (m); + if (_main_sound && !_main_sound->equals (other->_main_sound, opt, notes)) { + return false; } if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) { - o.push_back ("reel has different assets"); + notes.push_back ("reel has different assets"); + return false; } - if (_main_subtitle) { - list<string> m = _main_subtitle->equals (other->_main_subtitle, opt); - o.merge (m); + if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, notes)) { + return false; } - return o; + return true; } @@ -54,7 +54,7 @@ public: void write_to_cpl (std::ostream & s) const; - std::list<std::string> equals (boost::shared_ptr<const Reel> other, EqualityOptions opt) const; + bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, std::list<std::string>& notes) const; private: boost::shared_ptr<const PictureAsset> _main_picture; diff --git a/src/sound_asset.cc b/src/sound_asset.cc index 86ce69cf..9395a487 100644 --- a/src/sound_asset.cc +++ b/src/sound_asset.cc @@ -193,12 +193,14 @@ SoundAsset::write_to_cpl (ostream& s) const << " </MainSound>\n"; } -list<string> -SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const +bool +SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const { - list<string> notes = MXFAsset::equals (other, opt); + if (!MXFAsset::equals (other, opt, notes)) { + return false; + } - if (opt.flags & MXF_INSPECT) { + if (!opt.bitwise) { ASDCP::PCM::MXFReader reader_A; if (ASDCP_FAILURE (reader_A.OpenRead (path().string().c_str()))) { throw MXFFileError ("could not open MXF file for reading", path().string()); @@ -232,6 +234,7 @@ SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const ) { notes.push_back ("audio MXF picture descriptors differ"); + return false; } ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte); @@ -248,17 +251,17 @@ SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const if (buffer_A.Size() != buffer_B.Size()) { notes.push_back ("sizes of audio data for frame " + lexical_cast<string>(i) + " differ"); - continue; + return false; } if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) { notes.push_back ("PCM data for MXF frame " + lexical_cast<string>(i) + " differ"); - continue; + return false; } } } - return notes; + return true; } shared_ptr<const SoundFrame> diff --git a/src/sound_asset.h b/src/sound_asset.h index 3f42a522..a19d6726 100644 --- a/src/sound_asset.h +++ b/src/sound_asset.h @@ -87,7 +87,7 @@ public: */ void write_to_cpl (std::ostream& s) const; - std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const; + bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const; boost::shared_ptr<const SoundFrame> get_frame (int n) const; diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h index d02e364c..66f75cbe 100644 --- a/src/subtitle_asset.h +++ b/src/subtitle_asset.h @@ -183,9 +183,10 @@ public: SubtitleAsset (std::string directory, std::string xml); void write_to_cpl (std::ostream&) const {} - virtual std::list<std::string> equals (boost::shared_ptr<const Asset>, EqualityOptions) const { + virtual bool equals (boost::shared_ptr<const Asset>, EqualityOptions, std::list<std::string>& notes) const { /* XXX */ - return std::list<std::string> (); + notes.push_back ("subtitle assets not compared yet"); + return true; } std::string language () const { diff --git a/src/types.h b/src/types.h index 314b674b..7c98a3b3 100644 --- a/src/types.h +++ b/src/types.h @@ -85,15 +85,12 @@ public: extern bool operator== (Fraction const & a, Fraction const & b); extern bool operator!= (Fraction const & a, Fraction const & b); -enum EqualityFlags { - LIBDCP_METADATA = 0x1, - MXF_BITWISE = 0x2, - MXF_INSPECT = 0x4 -}; - struct EqualityOptions { - EqualityFlags flags; - bool verbose; + /** true to do a bitwise comparison. + * false to compare PCM and image data, possibly allowing + * some variation in values. + */ + bool bitwise; double max_mean_pixel_error; double max_std_dev_pixel_error; }; |
