1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 // Interface header.
31 #include "analysis.h"
32
33 // appleseed.foundation headers.
34 #include "foundation/image/canvasproperties.h"
35 #include "foundation/image/color.h"
36 #include "foundation/image/colorspace.h"
37 #include "foundation/image/image.h"
38 #include "foundation/image/tile.h"
39 #include "foundation/math/scalar.h"
40
41 // Standard headers.
42 #include <cassert>
43 #include <cmath>
44 #include <cstddef>
45
46 using namespace std;
47
48 namespace foundation
49 {
50
51 namespace
52 {
accumulate_luminance(const Tile & tile,double & accumulated_luminance,size_t & relevant_pixel_count)53 void accumulate_luminance(
54 const Tile& tile,
55 double& accumulated_luminance,
56 size_t& relevant_pixel_count)
57 {
58 assert(tile.get_channel_count() == 4);
59
60 const size_t tile_width = tile.get_width();
61 const size_t tile_height = tile.get_height();
62
63 for (size_t y = 0; y < tile_height; ++y)
64 {
65 for (size_t x = 0; x < tile_width; ++x)
66 {
67 // Fetch the pixel color; assume linear RGBA.
68 Color4f linear_rgba;
69 tile.get_pixel(x, y, linear_rgba);
70
71 // Extract the RGB part (ignore the alpha channel).
72 const Color3f linear_rgb = linear_rgba.rgb();
73
74 // Skip pixels containing NaN values.
75 if (has_nan(linear_rgb))
76 continue;
77
78 // Compute the Rec. 709 relative luminance of this pixel.
79 const float lum = luminance(clamp_low(linear_rgb, 0.0f));
80
81 // It should no longer be possible to have NaN at this point.
82 assert(lum == lum);
83
84 accumulated_luminance += static_cast<double>(lum);
85 ++relevant_pixel_count;
86 }
87 }
88 }
89
accumulate_luminance(const Image & image,double & accumulated_luminance,size_t & relevant_pixel_count)90 void accumulate_luminance(
91 const Image& image,
92 double& accumulated_luminance,
93 size_t& relevant_pixel_count)
94 {
95 accumulated_luminance = 0.0;
96 relevant_pixel_count = 0;
97
98 const CanvasProperties& props = image.properties();
99
100 for (size_t ty = 0; ty < props.m_tile_count_y; ++ty)
101 {
102 for (size_t tx = 0; tx < props.m_tile_count_x; ++tx)
103 {
104 const Tile& tile = image.tile(tx, ty);
105 accumulate_luminance(tile, accumulated_luminance, relevant_pixel_count);
106 }
107 }
108 }
109 }
110
compute_average_luminance(const Image & image)111 double compute_average_luminance(const Image& image)
112 {
113 double accumulated_luminance;
114 size_t relevant_pixel_count;
115
116 accumulate_luminance(image, accumulated_luminance, relevant_pixel_count);
117
118 return relevant_pixel_count > 0
119 ? accumulated_luminance / relevant_pixel_count
120 : 0.0;
121 }
122
are_images_compatible(const Image & image1,const Image & image2)123 bool are_images_compatible(const Image& image1, const Image& image2)
124 {
125 const CanvasProperties& props1 = image1.properties();
126 const CanvasProperties& props2 = image2.properties();
127
128 return
129 props1.m_canvas_width == props2.m_canvas_width &&
130 props1.m_canvas_height == props2.m_canvas_height;
131 }
132
compute_rms_deviation(const Image & image1,const Image & image2)133 double compute_rms_deviation(const Image& image1, const Image& image2)
134 {
135 if (!are_images_compatible(image1, image2))
136 throw ExceptionIncompatibleImages();
137
138 const CanvasProperties& props = image1.properties();
139 double mse = 0.0; // mean square error
140
141 for (size_t y = 0; y < props.m_canvas_height; ++y)
142 {
143 for (size_t x = 0; x < props.m_canvas_width; ++x)
144 {
145 Color3f color1;
146 image1.get_pixel(x, y, color1);
147
148 Color3f color2;
149 image2.get_pixel(x, y, color2);
150
151 mse += square_distance(color1, color2);
152 }
153 }
154
155 mse /= props.m_pixel_count * 3.0;
156
157 return sqrt(mse);
158 }
159
160 } // namespace foundation
161