0

I have an RGB image frame and I want to remove all pixels that are NOT a desired color.

How can do this without a for loop?

    % Desired color:   R 105, G 112, B 175
            % I want to zero all pixels that are not this color (plus a tad).

    red_target = 105;
    green_target  = 112;
    blue_target = 175;

    tad = 4;
    red_low = red_target - tad;
    red_hi = red_target + tad;
    green_low = green_target - tad;
    green_hi = green_target + tad;
    blue_low = blue_target - tad;
    blue_hi = blue_target + tad;


    % Filter out non-target colors:
    % Pixel redness is within target; greenness is within target; and blueness within:
    % Reset pixel if wrong redness OR wrong greenness OR wrong blueness:
    raw_frame_size = size( raw_frame )
    rows = raw_frame_size( 1 );
    columns = raw_frame_size( 2 );
    for row = 1:rows
        for column = 1:columns
            % Reset RGB pixel value if pixel is outside desired range:
            pixel_redness =  raw_frame(row,column,1);
            pixel_greenness =  raw_frame(row,column,2);
            pixel_blueness =  raw_frame(row,column,3);
            if  (      ( pixel_redness < red_low )  |  ( pixel_redness > red_hi ) ...
                    |  ( pixel_greenness < green_low )  |  ( pixel_greenness > green_hi  ) ...
                    |  ( pixel_blueness < blue_low )  |  ( pixel_blueness > blue_hi ) ) 
                raw_frame( row, column, 1 ) = 0;
                raw_frame( row, column, 2 ) = 0;
                raw_frame( row, column, 3 ) = 0;

            end
        end
    end
Doug Null
  • 7,989
  • 15
  • 69
  • 148
  • Look at this, and do the reverse: http://stackoverflow.com/questions/4063965/how-can-i-convert-an-rgb-image-to-grayscale-but-keep-one-color/4064205#4064205 – rayryeng Aug 26 '15 at 19:19
  • plus be careful about the use of `|` and `&`. I think you are missing a few `&` in your code. – Benoit_11 Aug 26 '15 at 19:31
  • Done. And I put in how I'd do it with for loops, which I hope to replace with a minimal number of statements. – Doug Null Aug 26 '15 at 19:46
  • @DougNull - again... look at this post: http://stackoverflow.com/questions/4063965/how-can-i-convert-an-rgb-image-to-grayscale-but-keep-one-color/4064205#4064205 ... and do the reverse. – rayryeng Aug 26 '15 at 19:55
  • It uses for loops, which was what I wanted to avoid. – Doug Null Aug 26 '15 at 22:51
  • 2
    @DougNull - That post doesn't use `for` loops at all. It transforms the image into HSV and does some clever selection in selecting out the dominant hue of a colour to maintain. In your case, you want to do the opposite and remove that dominant hue... – rayryeng Aug 27 '15 at 04:06

3 Answers3

3

From what I can tell is your objective, this is definitely achievable without for loops using logical indexing.

% Prepare Filter Arrays.
imageSize = size ( raw_frame );
filterArray  = zeros ( imageSize ( 1 ) , imageSize ( 2 ) );

% Identify filter pixels.
% This step utilizes logical indexing.
filterArray ( raw_frame(:,:,1) < red_low ) = 1;
filterArray ( raw_frame(:,:,1) > red_high ) = 1;
filterArray ( raw_frame(:,:,2) < green_low ) = 1;
filterArray ( raw_frame(:,:,2) > green_high ) = 1;
filterArray ( raw_frame(:,:,3) < blue_low ) = 1;
filterArray ( raw_frame(:,:,3) > blue_high) = 1;

% Replicate the array to 3D.
filter3d = repmat ( filterArray , [ 1 1 3 ]);

% Filter the image.
% This step also uses logical indexing.
raw_frame ( filter3d ) = 0;

Also, it is a generally bad practice to perform color filtering using an RGB image given the inability to isolate color from saturation and darkness using RGB values alone.

For instance, the RGB values [100,80,40] and [50,40,20] are the same color (hue) but different intensity (lightness or value). A better alternative may be to:

targetRgb(1,1,1) = 105;
targetRgb(1,1,2) = 112;
targetRgb(1,1,3) = 175;
targetHsv = rgb2hsv(targetRgb);

frameHsv = rgb2hsv(raw_frame);
frameSize = size(raw_frame);

hTad = 0.05;

% Values must be in the range [0,1] inclusive.
% Because "hue" is a "colorwheel", -0.3 would be the same value as 0.7.
targetHLow = ( targetHsv(1) - hTad + 1 ) - floor ( targetHsv(1) - hTad + 1);

% A value like 1.3 is the same  as 0.3
targetHHigh = ( targetHsv(1) + hTad ) - floor ( targetHsv(1) + hTad );

% Create the filter.
hFilter = zeros(frameSize(1),frameSize(2));
hFilter(hFilter<targetHLow) = 1;
hFilter(hFilter>targetHHigh) = 1;

% Zero out the value channel.
frameV = framHsv(:,:,3);
frameV(hFilter) = 0;
frameHsv(:,:,3) = frameV;

% Convert the modified HSV back to RGB.
frameFiltered = hsv2rgb(frameHsv);    
Juderb
  • 735
  • 3
  • 9
0

I think there is a much simpler, and computationally efficient implementation. The filter is a 2D logical array.

myrgb = rand(100,100,3);
imagesc(myrgb)
floatNorm = 2^8 - 1;
tad = repmat(4, [100, 100, 3]);
target = repmat([105,112,175], 100*100, 1);
target = reshape(target, [100, 100, 3]);
threshold = (target + tad)/floatNorm;
myFilt = abs(myrgb - 1/2) <= threshold;
myrgb(myFilt) = 0;
imagesc(myrgb)

before after

  • From what i can tell, the accepted range is one sided here. Also, it zeros reach individual channel, not all 3 if a single channel fails. – Juderb Aug 27 '15 at 11:40
-3

Can't be done without for loop. So here is my solution using for loops....

    % blue:   R 105, G 112, B 175

    red_target = 105;
    green_target  = 112;
    blue_target = 175;

    tad = 20;
    red_low = red_target - tad;
    red_hi = red_target + tad;
    green_low = green_target - tad;
    green_hi = green_target + tad;
    blue_low = blue_target - tad;
    blue_hi = blue_target + tad;


    % Filter out non-target colors:
    % Pixel redness is within target; greeness is within target; and blueness within:
    % Reset pixel if wrong redness OR wrong greenness OR wrong blueness:

    raw_frame_size = size( raw_frame )
    rows = raw_frame_size( 1 );
    columns = raw_frame_size( 2 );
    for row = 1:rows
        for column = 1:columns
            % Reset RGB pixel value if pixel is outside desired range:
            pixel_redness =  raw_frame(row,column,1);
            pixel_greenness =  raw_frame(row,column,2);
            pixel_blueness =  raw_frame(row,column,3);
            if  (      ( pixel_redness < red_low )  |  ( pixel_redness > red_hi ) ...
                    |  ( pixel_greenness < green_low )  |  ( pixel_greenness > green_hi  ) ...
                    |  ( pixel_blueness < blue_low )  |  ( pixel_blueness > blue_hi ) ) 
                raw_frame( row, column, 1 ) = 0;
                raw_frame( row, column, 2 ) = 0;
                raw_frame( row, column, 3 ) = 0;

            end
        end
    end
Doug Null
  • 7,989
  • 15
  • 69
  • 148
  • I'd like to know what your criteria was for you to determine that you couldn't do this without loops. The two loops near the end of your code can totally be done with a few logical statements. – rayryeng Aug 27 '15 at 13:46