libdcp
j2k_picture_asset.cc
1 /*
2  Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
6  libdcp is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  libdcp is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with libdcp. If not, see <http://www.gnu.org/licenses/>.
18 
19  In addition, as a special exception, the copyright holders give
20  permission to link the code of portions of this program with the
21  OpenSSL library under certain conditions as described in each
22  individual source file, and distribute linked combinations
23  including the two.
24 
25  You must obey the GNU General Public License in all respects
26  for all of the code used other than OpenSSL. If you modify
27  file(s) with this exception, you may extend this exception to your
28  version of the file(s), but you are not obligated to do so. If you
29  do not wish to do so, delete this exception statement from your
30  version. If you delete this exception statement from all source
31  files in the program, then also delete it here.
32 */
33 
34 
40 #include "compose.hpp"
41 #include "dcp_assert.h"
42 #include "equality_options.h"
43 #include "exceptions.h"
44 #include "j2k_transcode.h"
45 #include "openjpeg_image.h"
46 #include "j2k_picture_asset.h"
48 #include "util.h"
49 #include <asdcp/AS_DCP.h>
50 #include <asdcp/KM_fileio.h>
51 #include <libxml++/nodes/element.h>
52 #include <boost/filesystem.hpp>
53 #include <list>
54 #include <stdexcept>
55 
56 
57 using std::string;
58 using std::list;
59 using std::vector;
60 using std::max;
61 using std::pair;
62 using std::make_pair;
63 using std::shared_ptr;
64 using namespace dcp;
65 
66 
67 J2KPictureAsset::J2KPictureAsset(boost::filesystem::path file)
68  : PictureAsset(file)
69 {
70 
71 }
72 
73 
74 J2KPictureAsset::J2KPictureAsset(Fraction edit_rate, Standard standard)
75  : PictureAsset(edit_rate, standard)
76 {
77 
78 }
79 
80 
81 void
82 J2KPictureAsset::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc)
83 {
84  _size.width = desc.StoredWidth;
85  _size.height = desc.StoredHeight;
86  _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
87  _intrinsic_duration = desc.ContainerDuration;
88  _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator);
89  _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator);
90 }
91 
92 
93 bool
94 J2KPictureAsset::descriptor_equals (
95  ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, NoteHandler note
96  ) const
97 {
98  if (
99  a.EditRate != b.EditRate ||
100  a.SampleRate != b.SampleRate ||
101  a.StoredWidth != b.StoredWidth ||
102  a.StoredHeight != b.StoredHeight ||
103  a.AspectRatio != b.AspectRatio ||
104  a.Rsize != b.Rsize ||
105  a.Xsize != b.Xsize ||
106  a.Ysize != b.Ysize ||
107  a.XOsize != b.XOsize ||
108  a.YOsize != b.YOsize ||
109  a.XTsize != b.XTsize ||
110  a.YTsize != b.YTsize ||
111  a.XTOsize != b.XTOsize ||
112  a.YTOsize != b.YTOsize ||
113  a.Csize != b.Csize
114 // a.CodingStyleDefault != b.CodingStyleDefault ||
115 // a.QuantizationDefault != b.QuantizationDefault
116  ) {
117 
118  note (NoteType::ERROR, "video MXF picture descriptors differ");
119  return false;
120  }
121 
122  if (a.ContainerDuration != b.ContainerDuration) {
123  note (NoteType::ERROR, "video container durations differ");
124  }
125 
126 // for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) {
127 // if (a.ImageComponents[j] != b.ImageComponents[j]) {
128 // notes.pack_start ("video MXF picture descriptors differ");
129 // }
130 // }
131 
132  return true;
133 }
134 
135 
136 bool
137 J2KPictureAsset::frame_buffer_equals (
138  int frame, EqualityOptions const& opt, NoteHandler note,
139  uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
140  ) const
141 {
142  if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) {
143  note (NoteType::NOTE, "J2K identical");
144  /* Easy result; the J2K data is identical */
145  return true;
146  }
147 
148  /* Decompress the images to bitmaps */
149  auto image_A = decompress_j2k (const_cast<uint8_t*>(data_A), size_A, 0);
150  auto image_B = decompress_j2k (const_cast<uint8_t*>(data_B), size_B, 0);
151 
152  /* Compare them */
153 
154  vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3);
155  int d = 0;
156  int max_diff = 0;
157 
158  for (int c = 0; c < 3; ++c) {
159 
160  if (image_A->size() != image_B->size()) {
161  note (NoteType::ERROR, String::compose ("image sizes for frame %1 differ", frame));
162  return false;
163  }
164 
165  int const pixels = image_A->size().width * image_A->size().height;
166  for (int j = 0; j < pixels; ++j) {
167  int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]);
168  abs_diffs[d++] = t;
169  max_diff = max (max_diff, t);
170  }
171  }
172 
173  uint64_t total = 0;
174  for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
175  total += *j;
176  }
177 
178  double const mean = double (total) / abs_diffs.size ();
179 
180  uint64_t total_squared_deviation = 0;
181  for (auto j: abs_diffs) {
182  total_squared_deviation += pow (j - mean, 2);
183  }
184 
185  auto const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
186 
187  note (NoteType::NOTE, String::compose("mean difference %1 deviation %2", mean, std_dev));
188 
189  if (mean > opt.max_mean_pixel_error) {
190  note (
191  NoteType::ERROR,
192  String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame)
193  );
194 
195  return false;
196  }
197 
198  if (std_dev > opt.max_std_dev_pixel_error) {
199  note (
200  NoteType::ERROR,
201  String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame)
202  );
203 
204  return false;
205  }
206 
207  return true;
208 }
209 
210 
211 string
212 J2KPictureAsset::static_pkl_type (Standard standard)
213 {
214  switch (standard) {
215  case Standard::INTEROP:
216  return "application/x-smpte-mxf;asdcpKind=Picture";
217  case Standard::SMPTE:
218  return "application/mxf";
219  default:
220  DCP_ASSERT (false);
221  }
222 }
223 
224 
225 string
226 J2KPictureAsset::pkl_type (Standard standard) const
227 {
228  return static_pkl_type (standard);
229 }
A class to describe what "equality" means for a particular test.
A fraction (i.e. a thing with an integer numerator and an integer denominator).
Definition: types.h:168
J2KPictureAsset(boost::filesystem::path file)
std::string pkl_type(Standard standard) const override
int64_t _intrinsic_duration
Definition: picture_asset.h:90
DCP_ASSERT macro.
Class to describe what equality means when calling Asset::equals().
Exceptions thrown by libdcp.
J2KPictureAsset class.
J2KPictureAssetWriter and FrameInfo classes.
Methods to encode and decode JPEG2000.
Namespace for everything in libdcp.
Definition: array_data.h:50
std::shared_ptr< OpenJPEGImage > decompress_j2k(uint8_t const *data, int64_t size, int reduce)
OpenJPEGImage class.
Utility methods and classes.