So first, let me explain that the type of your data is, according to your types, an array of Uint8 values. The U here stands for unsigned, meaning the value can only be 0-255, and can't be negative in the first place. Therefore, taking the absolute value of an unsigned number doesn't do anything. And when you subtract unsigned types, the values wrap around. (e.g. 100u - 101u = 255u) This is probably what's causing part of the strange or erroneous behavior.
I always get confused by percent difference vs percent change, and things get hazy once you get close to 0, but I think in this situation
both are erroneous to use.
For example, let's say the Red component of the first image's pixel is 1, and the Red component of the second image's pixel is 2. This should totally be within the tolerance you're looking for... right?
But... the percent difference between 1 and 2 is
66.7%.
And the percent change from 1 to 2 is a
100% increase.
Neither of these make sense in the context of you comparing the images!
What I think you're actually trying to do is see if the difference between two values is within 10% of the possible range that those values could be. I'm not sure what the best way to phrase that is.
The range of your data is 0 to 255, inclusive, if we're working with Uint8. So, 10% of 255 is 25.5. If we round that to 26, that means your two pieces of data should be within 26 values to be considered "close enough" to each other.
Something like this. Notice we first convert the Uint8 data to a wider, signed type in order to be able to take the difference correctly. If you cannot cast, you must first find which value is bigger, and subtract the smaller value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
// Example program
#include <iostream>
#include <algorithm>
#include <string>
#include <cstdint>
using Uint8 = std::uint8_t;
const Uint8 RGB_Max = 255;
bool close_enough(Uint8 a, Uint8 b, double percent)
{
return abs(static_cast<int>(a) - static_cast<int>(b)) < static_cast<int>(percent * RGB_Max + 0.5);
}
bool close_enough_without_cast(Uint8 a, Uint8 b, double percent)
{
// http://www.cplusplus.com/reference/algorithm/max/
const Uint8& max_comp = std::max(a, b);
const Uint8& min_comp = std::min(a, b);
return max_comp - min_comp < static_cast<Uint8>(percent * RGB_Max + 0.5);
}
int main()
{
double percent = 0.10;
Uint8 fdata[] = {100, 200, 230, 50, 0, 255};
Uint8 pdata[] = {150, 190, 200, 70, 2, 250};
for (int j = 0; j < 6; j++)
{
Uint8 a = fdata[j];
Uint8 b = pdata[j];
// Converting to (int) just to print it out correctly
std::cout << (int)a << " " << (int)b << " -> ";
if (close_enough_without_cast(a, b, percent))
{
std::cout << " ... okay" << std::endl;
}
else
{
std::cout << " ... not close enough!" << std::endl;
}
}
}
|
Output:
1 2 3 4 5 6 7
|
Success #stdin #stdout 0s 4452KB
100 150 -> ... not close enough!
200 190 -> ... okay
230 200 -> ... not close enough!
50 70 -> ... okay
0 2 -> ... okay
255 250 -> ... okay
|
____________________________________________________
Note that the differences in the numerical value of a pixel is different than what our eyes actually perceive. The difference in perception between RGB(0, 0, 0) and RGB(10, 10, 10) is actually much greater than the difference between RGB(240, 240, 240) and RGB(250, 250, 250). Changes in dark values matter more than changes in light values. And once you add in color, things get even more complicated. Since these are DICOM files, I assume this is some medical-related situation you're in. To be the most accurate, you must account for the perceptual differences*.
Read about the gamma transfer function and search more stuff about it if you're interested.
https://en.wikipedia.org/wiki/SRGB
*I'm just talking about in general though. I don't actually know what the current medical standards are, DICOM or not, when dealing with RGB colors or different color spaces. Seek a professional for that :p)