Search code examples
imagematlabimage-processingnormalizationpattern-recognition

Detect repetitive pixel patterns in an image and remove them using matlab


I'm using Matlab R2017a and I have a RGB image (TIFF 128x128 uint16), shown below as a png image:

enter image description here

Actual TIFF image: http://s000.tinyupload.com/index.php?file_id=13823805859248753003

As shown above, there's a repeating pattern of really light pixels (yellow and light blue). Because I'm using pixel data, the really light pixels are skewing my graphs, so I want to "neutralize" them. I looked everywhere but I couldn't find a clean pattern recognition/removal set of commands, so I ended up finding the rows in the image where there were more than 10 pixels with intensity value > 1036 - there were 19 rows. From there, I found the indices where these brightest pixels occur, and stored them in a 19-cell cell array - cellarray{}. I can get those brightest pixel values by running image(cellarray{n}), where n goes from 1-19.

From here, I want to "neutralize" these super bright pixels by taking the average of the "normal" pixels above and below it. But if it is adjacent to another really bright pixel, I want its new pixel value to be the average of the closest pixels that are "normal". I hope that makes sense... Can someone help me with the code or suggest an easier method? Thanks so much!


Solution

  • Two methods are proposed, one using cross correlation the other using brightness. They work with both grayscale and multiband images. You should play with the settings a bit to improve the result.

    Important: fillmissing requires Matlab 2016b or newer

    Method A) Using cross correlation

    This works by extracting a single occurrence of the pattern and finding the location on the images where the correlation is very high. While it provides better results than Method B, it is also more complicated and needs a bit more knowledge about what you are doing:

    I = double(yourimage);
    % Show image
    imagesc(I)
    % You have to select a part of single occurrence of the pattern (a template) on the image! See below image.
    rect = round(getrect);
    % In case it is a multiband image make grayscale image
    if size(I,3)>1
        BW = rgb2gray(I);
    else
        BW = I;
    end
    % Extract template from BW
    template = BW(rect(2):rect(2)+rect(4),rect(1):rect(1)+rect(3),:);
    % Show template - this is the extent you selected during "getrect"
    imagesc(template)
    

    template

    % Calculate how much said template correlates on each pixel in the image
    C = normxcorr2(template,BW);
    % Remove padded borders from correlation
    pad = floor(size(template)./2);
    center = size(I);
    C = C([false(1,pad(1)) true(1,center(1))], ...
            [false(1,pad(2)) true(1,center(2))]);
    % Plot the correlation
    figure, surf(C), shading flat
    

    Correlation of the template on the image. Note that it both highly correlates with the bright yellow patterns and the light blue pattern bellow.

    correlation

    % Get all indexes where the correlation is high. Value read from previous figure.
    % The lower the cut-off value, the more pixels will be altered
    idx = C>0.5;
    % Dilate the idx because else masked area is too small
    idx = imdilate(idx,strel('disk',1));
    % Replicate them if multiband image. Does nothing if only grayscale image
    idx = repmat(idx,1,1,size(I,3));
    % Replace pattern pixels with NaN
    I(idx) = NaN;
    % Fill Nan values with 4x4 median filter
    I = fillmissing(I,'movmedian',[4 4]);
    % Display new image
    figure; imagesc(I)
    

    endresult

    It catches both the yellow and light blue pattern but also some false positives. You have to experiment with different templates, cut-off values, dilation radii and median filter sizes to improve the result.

    Method B) Using brightness of image

    A bit offtopic because no pattern recognition is used but instead that the yellow patterns are just very bright. But since the result isn't too bad and a lot simpler I felt it might be useful. A lot easier to avoid finding false positives.

    % I = your image
    I = double(I);
    
    % get indexes where very bright in red channel
    idx = cdata(:,:,1)>157; % 157 = brightest non-pattern pixel in your image
    
    % From now on same as end from method A)!
    % dilate the idx to also get the adjacent pixels because else too few pixels will be erased
    idx = imdilate(idx,strel('disk',1));
    % replacate them if multiband image. Does nothing if only grayscale image
    idx = repmat(idx,1,1,size(I,3));
    % replace pattern pixels with NaN
    I(idx) = NaN;
    % fill Nan values using 50x50 median filter
    I = fillmissing(I,'movmedian',[50 50]);
    % display new image
    figure; imagesc(I)
    

    enter image description here