Make Matlab issue a warning when converting a double to a uint8 and vice versa? - matlab

Typically in Matlab colours are represented by three element vectors of RGB intensity values, with precision uint8 (range 0 - 255) or double (range 0 - 1). Matlabs functions such as imshow work with either representation making both easy to use in a program.
It is equally easy however to introduce a bug when assigning colour values from a matrix of one type, to that of another (because the value is converted silently, but not re-scaled to the new range). Having just spent a number of hours finding such a bug, I would like to make sure it is never introduced again.
How do I make Matlab display a warning when type conversion takes place?
Ideally it would only be when the conversion is between double and uint8. It should also be difficult to deactivate (i.e. the option is not reset when loading a workspace, or when matlab crashes).

A possible solution is to define your own uint8 function that casts to uint8 and issues a warning if some value has been truncated.
You should place this function in a folder where it shadows the builtin uint8 funciton. For example, your user folder is a good choice, as it usually appears the first in path.
Or, as noted by Sam Roberts, if you want this function to be called only when converting from double into uint8 (not when converting from any other type into uint8), put it in a folder named #double within your path.
function y = uint8(x)
y = builtin('uint8', x);
if any(x(:)>255) || any(x(:)<0)
warning('MATLAB:castTruncation', 'Values truncated during conversion to uint8')
end
The warning is on by default. You can switch it on or off with the commands warning('on','MATLAB:castTruncation') and warning('off','MATLAB:castTruncation') (thanks to CitizenInsane for the suggestion).

Related

Matlab-how can I use SSIM function using sprintf function

Now I'm researching on Image processing about interpolation. So I invented my new interpolation algorithms about image.
And I have to check SSIM value about my images that are interpolated by my interpolation algorithms.
Now I'm using ICY tool (On windows, GUI environment) but this tool is too hard to check large amount of images because of GUI interface.
So I want to check image SSIM value using matlab ssim function.
but I'm not a expert in matlab language, so I'm in trouble in dealing with import various name to ssim function.
This is what I want to do:
I want to checks ssim value with a large amount of images.
So I'm going to use sprintf function to make a valuable image name.
The third I want to import this input image name to ssim function.
This is the code I used.
for n= 1 : 10
str = sprintf('./x2/cutted/x2_fn%d_4p_3p_cutted.bmp',n);
str_ori = sprintf('ori_%d.bmp',n);
img_cutted=imread(str);
img_ori=(str_ori);
[ssimval,ssimmap]=ssim(str,str_ori);
end
When I use this code, the error is invoked.
Error: The str must be the types of uint8, uint16, int16, single, double.
but you used char value.
After I got this message, write "uint8 str;" but still the message is invoked.
ssim gets 2 images as input, not 2 strings. replace your code by [ssimval,ssimmap]=ssim(img,img_ori);

Accuracy error in binomials using MATLAB?

The value is absolute integer, not a floating point to be doubted, also, it is not about an overflow since a double value can hold until 2^1024.
fprintf('%f',realmax)
179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
The problem I am facing in nchoosek function that it doesn't produce exact values
fprintf('%f\n',nchoosek(55,24));
2488589544741302.000000
While it is a percentage error of 2 regarding that binomian(n,m)=binomial(n-1,m)+binomial(n-1,m-1) as follows
fprintf('%f',nchoosek(55-1,24)+nchoosek(55-1,24-1))
2488589544741301.000000
Ps: The exact value is 2488589544741300
this demo shows
What is wrong with MATLAB?
Your understanding of the realmax function is wrong. It's the maximum value which can be stored, but with such large numbers you have a floating point precision error far above 1. The first integer which can not be stored in a double value is 2^53+1, try 2^53==2^53+1 for a simple example.
If the symbolic toolbox is available, the easiest to implement solution is using it:
>> nchoosek(sym(55),sym(24))
ans =
2488589544741300
There is a difference between something that looks like an integer (55) and something that's actually an integer (in terms of variable type).
The way you're calculating it, your values are stored as floating point (which is what realmax is pointing you to - the largest positive floating point number - check intmax('int64') for the largest possible integer value), so you can get floating point errors. An absolute difference of 2 in a large value is not that unexpected - the actual percentage error is tiny.
Plus, you're using %f in your format string - e.g. asking it to display as floating point.
For nchoosek specifically, from the docs, the output is returned as a nonnegative scalar value, of the same type as inputs n and k, or, if they are different types, of the non-double type (you can only have different input types if one is a double).
In Matlab, when you type a number directly into a function input, it generally defaults to a float. You have to force it to be an integer.
Try instead:
fprintf('%d\n',nchoosek(int64(55),int64(24)));
Note: %d not %f, converting both inputs to specifically integer. The output of nchoosek here should be of type int64.
I don't have access to MATLAB, but since you're obviously okay working with Octave I'll post my observations based on that.
If you look at the Octave source code using edit nchoosek or here you'll see that the equation for calculating the binomial coefficient is quite simple:
A = round (prod ((v-k+1:v)./(1:k)));
As you can see, there are k divisions, each with the possibility of introducing some small error. The next line attempts to be helpful and warn you of the possibility of loss of precision:
if (A*2*k*eps >= 0.5)
warning ("nchoosek", "nchoosek: possible loss of precision");
So, if I may slightly modify your final question, what is wrong with Octave? I would say nothing is wrong. The authors obviously knew of the possibility of imprecision and included a check to warn users when that possibility arises. So the function is working as intended. If you require greater precision for your application than the built-in function provides, it looks as though you'll need to code (or find) something that calculates the intermediate results with greater precision.

uint64 is not exact for vectors in Matlab

I have discovered an inconsistency for uint64 when using vectors in Matlab. It seems as an array of uint64 is not exact for all 64 bits. This did not give the output I expected,
p=uint64([0;0]);
p(1)=13286492335502040542
p =
13286492335502041088
0
However
q = uint64(13286492335502040542)
q =
13286492335502040542
does. It is also working with
p(1)=uint64(13286492335502040542)
p =
13286492335502040542
0
Working with unsigned integers one expect a special behaviour and usually also perfect precision. This seems weird and even a bit uncanny. I do not see this problem with smaller numbers. Maybe anyone knows more? I do not expect this to be an unknown problem, so I guess there must be some explanation to it. I would be good to know why this happen and when, to be able to avoid it. As usual this kind of issues is mentioned nowhere in the documentation.
Matlab 2014a, windows 7.
EDIT
It is worth mentioning that I can see the same behaviour when defining arrays directly.
p=uint64([13286492335502040542;13286492335502040543])
p =
13286492335502041088
13286492335502041088
This is the root to why I ask this question. I have hard to see workaround for this case.
While it might be surprising, this is a floating point precision issue. :-)
The thing is, all numeric literals are by default of type double in MATLAB; that's why:
13286492335502040542 == 13286492335502041088
will return true; the floating point representation in double precision of 13286492335502040542 is 13286492335502041088. Since p has the class uint64, all assignments done to it will cast the right-hand-side to its class.
On another hand, the uint64(13286492335502040542) "call" will be optimized by the MATLAB interpreter to avoid the overhead of calling the uint64 function for the double argument, and will convert the literal directly to its unsigned integer representation (which is exact).
On a third hand [sic], the function call optimization doesn't apply to
p = uint64([13286492335502040542;13286492335502040543])
because the argument of uint64 is not a literal, but the result of an expression, i.e. the result of the vertcat operator applied to two double operands. In this case the MATLAB interpreter is not smart enough to figure out that the two function calls should "commute" (concatenation of uint should be the same as uint of concatenation), so it evaluates the concatenation (which gives an array of equal double because FP precision), then converts the two similar double values to uint64.
TLDR: the difference between
p = uint64(13286492335502040542);
and
u = 13286492335502040542; p = uint64(u);
is a side effect of function call optimization.
Matlab, unless told otherwise reads numbers as double, then casts to the relevant datatype. The Matlab double datatype allows for 51 bits for the floating point fraction, giving the possibility to store 52 bit integers without loss of prepossession (mantissa). Notice that 13286492335502041088 is just 13286492335502040543 with the last 12 bits set to zero.
the solution as you said, is to convert the literals directly uint64(13286492335502040543).
p=uint64([13286492335502040542;13286492335502040543]) does not work because it creates a double array and then converts it to uint64
This issue is mentioned in the uint64 documentation, under 'More About', although it doesn't mention that laterals are read as doubles unless otherwise specified.
I agree this seems weird and I don't have an explanation. I do have a workaround:
p=[uint64(13286492335502040542);uint64(13286492335502040543)]
i.e., cast the separate values to uint64s.

Why is find in matlab returning double values

The find function within matlab returns indices where the given locigal argument evaluates true.
Thus I'm wondering, why the return values (for the indices) are of type double and not uint32 or uint64 like the biggest index into a matrix could be.
Another strange thing which might be connected to that here is, that running
[~,max_num_of_elem]=computer
returns the maximal number of elements allowed for a matrix in the variable max_num_of_elem which is also of type double.
I can only guess, but probably because a wide range of functions only support double. Run
setdiff(methods('double'), methods('uint32'))
to see what functions are defined for double and not for uint32 on your version of MATLAB.
Also there is the overflow issue with integer data types in MATLAB that can introduce some hard to detect bugs.

Type casting a vector in Matlab

version: Matlab 2009a
I am generating a vector of size <1x116286> using randsrc() function. Since I am again adding it to the matrix of same size but of uint8 type, I am doing as follows -
l=typecast(randsrc(1,v(2)),'uint8');
Now, Matlab has changed the returned a vector of elements - [240,63,0] instead of [-1,1], with the size of <1x930288 uint8>. This is expected as double and uint8 has different size, but I want a vector of same size and values after type casting.
PS: I want to subtract or add '1' from all trhe values on a matrix of size <1x116286>. Is there any other neat way to do this?
As I understand the problem, there are a couple of issues with the above:
uint8 is an unsigned type so will not support a negative offset;
the "typecast" function is used to reinterpret existing data, not to convert it: here you are interpreting each byte of the floating-point output of randsrc(...) as an integer.
Unfortunately I don't have Matlab handy to test, but the following should provide something closer to what you are after:
l = int8( randsrc(1,v(2)) );
well instead of forming a vector (-1,1...) and adding it to some vector 'z' , I did something like this.
l =randsrc(1,v(2));
z(l==-1)=z(l==-1)-1;
z(l==1)=z(l==1)+1;
So, I now, don't need to change types.