Search code examples
imagematlabimage-processingmatlab-figure

How to calculate the mean of 3D matrices in an image without NaN?


I need to calculate the mean of a 3D matrices (last step in the code). However, there are many NaNs in the (diff_dataframe./dataframe_vor) calculation. So when I use this code, some results will be NaN. How could I calculate the mean of this matrix by ignoring the NaNs? I attached the code as below.

S.amplitude = 1:20;%:20;
S.blocksize = [1 2 3 4 5 6 8 10 12 15 20];
S.frameWidth = 1920;
S.frameHeight = 1080;
S.quality=0:10:100;
image = 127*ones(S.frameHeight,S.frameWidth,3);
S.yuv2rgb = [1 0 1.28033; 1 -0.21482 -0.38059; 1 2.12798 0];
i_bs = 0;
for BS = S.blocksize
i_bs = i_bs + 1;

hblocks = S.frameWidth / BS;
vblocks = S.frameHeight / BS;
i_a = 0;
dataU = randi([0 1],vblocks,hblocks);
dataV = randi([0 1],vblocks,hblocks);
dataframe_yuv = zeros(S.frameHeight, S.frameWidth, 3);
for x = 1 : hblocks
    for y = 1 : vblocks
        dataframe_yuv((y-1)*BS+1:y*BS, ...
            (x-1)*BS+1:x*BS, 2) = dataU(y,x) * 2 - 1;
        dataframe_yuv((y-1)*BS+1:y*BS, ...
            (x-1)*BS+1:x*BS, 3) = dataV(y,x) * 2 - 1;
    end
end

dataframe_rgb(:,:,1) = S.yuv2rgb(1,1) * dataframe_yuv(:,:,1) + ...
    S.yuv2rgb(1,2) * dataframe_yuv(:,:,2) + ...
    S.yuv2rgb(1,3) * dataframe_yuv(:,:,3);
dataframe_rgb(:,:,2) = S.yuv2rgb(2,1) * dataframe_yuv(:,:,1) + ...
    S.yuv2rgb(2,2) * dataframe_yuv(:,:,2) + ...
    S.yuv2rgb(2,3) * dataframe_yuv(:,:,3);
dataframe_rgb(:,:,3) = S.yuv2rgb(3,1) * dataframe_yuv(:,:,1) + ...
    S.yuv2rgb(3,2) * dataframe_yuv(:,:,2) + ...
    S.yuv2rgb(3,3) * dataframe_yuv(:,:,3);
for A = S.amplitude
    i_a = i_a + 1;
    i_q = 0;
image1p = round(image + dataframe_rgb * A);
image1n = round(image - dataframe_rgb * A);
dataframe_vor = ((image1p-image1n)/2)/255;
for Q = S.quality
        i_q = i_q + 1;
namestrp = ['greyjpegs/Img_BS' num2str(BS) '_A' num2str(A) '_Q'     num2str(Q) '_1p.jpg'];
namestrn = ['greyjpegs/Img_BS' num2str(BS) '_A' num2str(A) '_Q' num2str(Q) '_1n.jpg'];
imwrite(image1p/255,namestrp,'jpg', 'Quality', Q);
imwrite(image1n/255,namestrn,'jpg', 'Quality', Q);
error_mean(i_bs, i_a, i_q) = mean2((abs(diff_dataframe./dataframe_vor)));
end
end
end

Solution

  • mean2 is a shortcut function that's part of the image processing toolbox that finds the entire average of a 2D region which doesn't include handling NaN. In that case, simply remove all values that are NaN and find the resulting average. Note that the removal of NaN unrolls the 2D region into a 1D vector, so we can simply use mean in this case. As an additional check, let's make sure there are no divide by 0 errors, so also check for Inf as well.

    Therefore, replace this line:

    error_mean(i_bs, i_a, i_q) = mean2((abs(diff_dataframe./dataframe_vor)));
    

    ... with:

    tmp = abs(diff_dataframe ./ dataframe_vor);
    mask = ~isnan(tmp) | ~isinf(tmp);
    tmp = tmp(mask);
    
    if isempty(tmp)
        error_mean(i_bs, i_a, i_q) = 0;
    else
        error_mean(i_bs, i_a, i_q) = mean(tmp);
    

    We first assign the desired operation to a temporary variable, use isnan and isinf to remove out the offending values, then find the average of the rest. One intricacy is that if your entire region is NaN or Inf, then the removal of all these entries in the region results in the empty vector, and finding the mean of this undefined. A separate check is there to be sure that if it's empty, simply assign the value of 0 instead.