s:
It's a vectorized implementation of LBP, rather well-suited for Matlab.
After the initialization instructions, let's look the main loop, beginning at the line "for i = 1:neighbors
". The loop is pretty clear: it computes the comparison of one neighbor with the center pixel, the loop iterates over all neighbors. You've got this point, so now enter deep into the loop to understand how it accumulates all results.
The core of the loop is in fact over complicated because it takes into account the real circle instead of an approximate integer circle. So the purpose of the major part of the instructions is to compute the interpolated intensity of the neighbor pixel. Here it differs from the C++ code you have as reference, where it takes only the integer, 1-pixel-wide-radius circle. Remember that with the lbp.m code you can -- theoretically, I will discuss that later -- compute the LBP along a circle of radius R with N sampling points, so the C++ would correspond to a circle of radius 1 and with 8 sampling points, if only there was no interpolation. But there is an interpolation when the neighbor does not fit the pixel grid of the image, when (abs(x - rx) < 1e-6) && (abs(y - ry) < 1e-6)
is false).
If (abs(x - rx) < 1e-6) && (abs(y - ry) < 1e-6)
is true, there is no interpolation, so the computation of all comparisons between the central pixel and the current neighbor is stored directly into D
. Else, it computes a bilinear interpolation of the intensity at the sampling neighbor point, over the entire image: N = w1*d_image(fy:fy+dy,fx:fx+dx) + w2*d_image(fy:fy+dy,cx:cx+dx) + w3*d_image(cy:cy+dy,fx:fx+dx) + w4*d_image(cy:cy+dy,cx:cx+dx);
.
And finally, turn to the update part: v = 2^(i-1); result = result + v*D;
. v
is the equivalent of the shift: for the ith neighbor, you shift the value of the comparison by i-1
to the left, or equivalently multiplying be 2^(i-1)
. Then you sum with result
. So at the end of the loop, the computation is really equivalent to your C++ code, except that it's done over the entire image instead of one single pixel. And the C++ code can be seen as a unrolled version of the matlab loop with the neighbor circle of radius 1 and 8 sampling points. At this point, the LBP map is computed, the following blocks are additional processing of the LBP map (remap through a mapping table, and optionally computing the histogram of the LBP image instead of the LBP image itself).
Now, a little discussion about the whole script. There is a flaw here that is hidden at the end of the script. In fact, through the code, you are limited to 32 neighbors, no more, because at the end the LBP image is cast to int32
. The flaw is that the variable result
is allocated as a double matrix and not an integer matrix, so I really hope that there is no approximation problem when updating result
and later when casting into integer, leading to changing bits in the LBP. Normally there should not be as there is at least 52 precision bits (according to wikipedia for IEEE 754 spec). I think it's risky here ... and on the contrary I am not aware of a matlab type for long fixed-sized, efficient bit vector. I would use int64
instead of int32
, but the limit will be there at 64 sampling neighbors.
EDIT
Now, if your wish is to commpute some local binary patterns restricted on the 3*3 neighborhood, this Matlab function is way too generic for you, and the best thing is to unroll the loop for this neighborhood, and thus be really close to the C++ code. Here is a piece of code for that (I use bitwise or instead of addition, but it's equivalent):
result = uint8(ysize, xsize);
result = (image(1:end-2,2:end-1) > image(2:end-1,2:end-1)); % <=> v += (img(y-1,x ) > c) << 0;
result = result|bitshift((image(1:end-2,3:end) > image(2:end-1,2:end-1)), 1, 'uint8'); % <=> v += (img(y-1,x+1) > c) << 1;
result = result|bitshift((image(2:end-1,3:end) > image(2:end-1,2:end-1)), 2, 'uint8'); % <=> v += (img(y ,x+1) > c) << 2;
result = result|bitshift((image(3:end,3:end) > image(2:end-1,2:end-1)), 3, 'uint8'); % <=> v += (img(y+1,x+1) > c) << 3;
result = result|bitshift((image(3:end,2:end-1) > image(2:end-1,2:end-1)), 4, 'uint8'); % <=> v += (img(y+1,x ) > c) << 4;
result = result|bitshift((image(3:end,1:end-2) > image(2:end-1,2:end-1)), 5, 'uint8'); % <=> v += (img(y+1,x-1) > c) << 5;
result = result|bitshift((image(2:end-1,3:end) > image(2:end-1,2:end-1)), 6, 'uint8'); % <=> v += (img(y ,x-1) > c) << 6;
result = result|bitshift((image(1:end-2,1:end-2) > image(2:end-1,2:end-1)), 7, 'uint8'); % <=> v += (img(y-1,x-1) > c) << 7;
It's the exact translation of the C code to a Matlab script, using the powerful vectorization. With this in hand, it's pretty simple to change for another order or different tests in this neighborhood. I also mention this point because there is an error in the Matlab script for this case, line 53 there is a wrong sign: neighobrhood is better asspoints=[-1 -1; -1 0; -1 1; 0 -1; 0 -1; 1 -1; 1 0; 1 1];
instead of spoints=[-1 -1; -1 0; -1 1; 0 -1; -0 1; 1 -1; 1 0; 1 1];
.