Developer documentation
Version 3.0.3-105-gd3941f44
image_diff.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __image_check__h__
18#define __image_check__h__
19
20#include <set>
21
22#include "image_helpers.h"
23#include "algo/loop.h"
24
25namespace MR
26{
27
29 template <class HeaderType1, class HeaderType2>
30 inline void check_headers (HeaderType1& in1, HeaderType2& in2)
31 {
32 check_dimensions (in1, in2);
33 for (size_t i = 0; i < in1.ndim(); ++i) {
34 if (std::isfinite (in1.spacing(i)))
35 if (abs ((in1.spacing(i) - in2.spacing(i)) / (in1.spacing(i) + in2.spacing(i))) > 1e-4)
36 throw Exception ("images \"" + in1.name() + "\" and \"" + in2.name() + "\" do not have matching voxel spacings on axis " + str(i) +
37 " (" + str(in1.spacing(i)) + " vs " + str(in2.spacing(i)) + ")");
38 }
39 for (size_t i = 0; i < 3; ++i) {
40 for (size_t j = 0; j < 4; ++j) {
41 if (abs (in1.transform().matrix()(i,j) - in2.transform().matrix()(i,j)) > 0.001)
42 throw Exception ("images \"" + in1.name() + "\" and \"" + in2.name() + "\" do not have matching header transforms:\n" +
43 str(in1.transform().matrix()) + "\nvs:\n " + str(in2.transform().matrix()) + ")");
44 }
45 }
46 }
47
48
50 template <class ImageType1, class ImageType2>
51 inline void check_images_abs (ImageType1& in1, ImageType2& in2, const double tol = 0.0)
52 {
53 check_headers (in1, in2);
54 ThreadedLoop (in1)
55 .run ([&tol] (const ImageType1& a, const ImageType2& b) {
56 if (abs (cdouble (a.value()) - cdouble (b.value())) > tol)
57 throw Exception ("images \"" + a.name() + "\" and \"" + b.name() + "\" do not match within absolute precision of " + str(tol)
58 + " (" + str(cdouble (a.value())) + " vs " + str(cdouble (b.value())) + ")");
59 }, in1, in2);
60 }
61
62
64 template <class ImageType1, class ImageType2>
65 inline void check_images_frac (ImageType1& in1, ImageType2& in2, const double tol = 0.0) {
66
67 check_headers (in1, in2);
68 ThreadedLoop (in1)
69 .run ([&tol] (const ImageType1& a, const ImageType2& b) {
70 if (abs ((cdouble (a.value()) - cdouble (b.value())) / (0.5 * (cdouble (a.value()) + cdouble (b.value())))) > tol)
71 throw Exception ("images \"" + a.name() + "\" and \"" + b.name() + "\" do not match within fractional precision of " + str(tol)
72 + " (" + str(cdouble (a.value())) + " vs " + str(cdouble (b.value())) + ")");
73 }, in1, in2);
74 }
75
77 template <class ImageType1, class ImageType2, class ImageTypeTol>
78 inline void check_images_tolimage (ImageType1& in1, ImageType2& in2, ImageTypeTol& tol) {
79
80 check_headers (in1, in2);
81 check_headers (in1, tol);
82 ThreadedLoop (in1)
83 .run ([] (const ImageType1& a, const ImageType2& b, const ImageTypeTol& t) {
84 if (abs (cdouble (a.value()) - cdouble (b.value())) > t.value())
85 throw Exception ("images \"" + a.name() + "\" and \"" + b.name() + "\" do not match within precision of \"" + t.name() + "\""
86 + " (" + str(cdouble (a.value())) + " vs " + str(cdouble (b.value())) + ", tolerance " + str(t.value()) + ")");
87 }, in1, in2, tol);
88 }
89
91 template <class ImageType1, class ImageType2>
92 inline void check_images_voxel (ImageType1& in1, ImageType2& in2, const double tol = 0.0)
93 {
94 auto func = [&tol] (decltype(in1)& a, decltype(in2)& b) {
95 double maxa = 0.0, maxb = 0.0;
96 for (auto l = Loop(3) (a, b); l; ++l) {
97 maxa = std::max (maxa, abs (cdouble(a.value())));
98 maxb = std::max (maxb, abs (cdouble(b.value())));
99 }
100 const double threshold = tol * 0.5 * (maxa + maxb);
101 for (auto l = Loop(3) (a, b); l; ++l) {
102 if (abs (cdouble (a.value()) - cdouble (b.value())) > threshold)
103 throw Exception ("images \"" + a.name() + "\" and \"" + b.name() + "\" do not match within " + str(tol) + " of maximal voxel value"
104 + " (" + str(cdouble (a.value())) + " vs " + str(cdouble (b.value())) + ")");
105 }
106 };
107
108 ThreadedLoop (in1, 0, 3).run (func, in1, in2);
109 }
110
112 template <class HeaderType1, class HeaderType2>
113 inline void check_keyvals (const HeaderType1& in1, const HeaderType2& in2)
114 {
115 const static std::set<std::string> reserved { "command_history", "mrtrix_version", "project_version" };
116 auto it1 = in1.keyval().cbegin();
117 auto it2 = in2.keyval().cbegin();
118 Exception errors;
119 while (it1 != in1.keyval().end() || it2 != in2.keyval().end()) {
120 while (it1 != in1.keyval().end() && reserved.find (it1->first) != reserved.end())
121 ++it1;
122 while (it2 != in2.keyval().end() && reserved.find (it2->first) != reserved.end())
123 ++it2;
124
125 if (it1 == in1.keyval().end() || it2 == in2.keyval().end())
126 break;
127
128 if (it1 == in1.keyval().end()) {
129 errors.push_back ("Key \"" + it2->first + "\" in image \"" + in2.name() + "\" not present in \"" + in1.name() + "\"");
130 ++it2;
131 }
132 else if (it2 == in2.keyval().end()) {
133 errors.push_back ("Key \"" + it1->first + "\" in image \"" + in1.name() + "\" not present in \"" + in2.name() + "\"");
134 ++it1;
135 }
136 else if (it1->first < it2->first) {
137 errors.push_back ("Key \"" + it1->first + "\" in image \"" + in1.name() + "\" not present in \"" + in2.name() + "\"");
138 ++it1;
139 }
140 else if (it1->first > it2->first) {
141 errors.push_back ("Key \"" + it2->first + "\" in image \"" + in2.name() + "\" not present in \"" + in1.name() + "\"");
142 ++it2;
143 }
144 else {
145 if (it1->second != it2->second)
146 errors.push_back ("Key \"" + it1->first + "\" has different values between images");
147 ++it1;
148 ++it2;
149 }
150 }
151
152 if (errors.num() > 0)
153 throw errors;
154 }
155
157 template <class HeaderType1, class HeaderType2>
158 inline bool headers_match (HeaderType1& in1, HeaderType2& in2)
159 {
160 if (!dimensions_match (in1, in2))
161 return false;
162 if (!spacings_match (in1, in2, 1e-6)) // implicitly checked in voxel_grids_match_in_scanner_space but with different tolerance
163 return false;
164 if (!voxel_grids_match_in_scanner_space (in1, in2))
165 return false;
166 return true;
167 }
168
169
171 template <class ImageType1, class ImageType2>
172 inline bool images_match_abs (ImageType1& in1, ImageType2& in2, const double tol = 0.0)
173 {
174 if (!headers_match (in1, in2))
175 return false;
176 for (auto i = Loop (in1)(in1, in2); i; ++i)
177 if (abs (cdouble (in1.value()) - cdouble (in2.value())) > tol)
178 return false;
179 return true;
180 }
181
182
183
184
185
186
187}
188
189#endif
190
191
192
193
194
void push_back(const std::string &s)
Definition: exception.h:102
size_t num() const
Definition: exception.h:96
FORCE_INLINE LoopAlongAxes Loop()
Definition: loop.h:419
constexpr double e
Definition: math.h:39
Definition: base.h:24
bool images_match_abs(ImageType1 &in1, ImageType2 &in2, const double tol=0.0)
check images are the same within a absolute tolerance
Definition: image_diff.h:172
std::complex< double > cdouble
Definition: types.h:217
constexpr std::enable_if< std::is_arithmetic< X >::value &&std::is_unsigned< X >::value, X >::type abs(X x)
Definition: types.h:297
bool headers_match(HeaderType1 &in1, HeaderType2 &in2)
check image headers are the same (dimensions, spacing & transform)
Definition: image_diff.h:158
void check_images_frac(ImageType1 &in1, ImageType2 &in2, const double tol=0.0)
check images are the same within a fractional tolerance
Definition: image_diff.h:65
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
ThreadedLoopRunOuter< decltype(Loop(vector< size_t >()))> ThreadedLoop(const HeaderType &source, const vector< size_t > &outer_axes, const vector< size_t > &inner_axes)
Multi-threaded loop object.
void check_headers(HeaderType1 &in1, HeaderType2 &in2)
check image headers are the same (dimensions, spacing & transform)
Definition: image_diff.h:30
void check_images_tolimage(ImageType1 &in1, ImageType2 &in2, ImageTypeTol &tol)
check images are the same within a tolerance defined by a third image
Definition: image_diff.h:78
void check_images_abs(ImageType1 &in1, ImageType2 &in2, const double tol=0.0)
check images are the same within a absolute tolerance
Definition: image_diff.h:51
void check_keyvals(const HeaderType1 &in1, const HeaderType2 &in2)
check headers contain the same key-value entries
Definition: image_diff.h:113
void check_images_voxel(ImageType1 &in1, ImageType2 &in2, const double tol=0.0)
check images are the same within a fractional tolerance relative to the maximum value in the voxel
Definition: image_diff.h:92