-1
$\begingroup$

How would one find the pixels that match an RGB value in a matrix in MATLAB?

Let's suppose that I want to find the image coordinates of pixels whose value is $RGB=(144,127,11)$ for a given image in a 3d matrix format.

Is there any built-in function in MATLAB that implements this search without explicitly looping over each dimension of the matrix, thus implicitly speeding up the search by considering a vectorized approach?

$\endgroup$
2
$\begingroup$

Given a RGB value as a 1d array you can get the pixel's image coordinates using a combination of the following built-in MATLAB functions find, prod and reshape:

[row, col] = find(prod(img == reshape(val, 1, 1, 3), 3)) 

where img is a $N\times M\times 3$ image array and val is a 1d array with 3 elements $1\times 3$ or $3\times 1$, it's the RGB color value of the pixel that we want to find.

For example reshape([144, 127, 11], 1, 1, 3) reorganizes or reshapes the $1\times 3$ array into a $1\times 1\times 3$ 3d array to match the dimensions of the img array.

This is gives us the advantage of using the == as if we had explicitly called the following

im(:,:,1) == v(1)
im(:,:,2) == v(2)
im(:,:,3) == v(3)

Then prod(img == reshape(val, 1, 1, 3), 3, 3) takes the elementwise product of each of these 2d arrays that contain truth values yielding a single 2d array that contains truth values (0 or 1), this is equivalent to calling

im(:,:,1) == v(1) & im(:,:,2) == v(2) & im(:,:,3) == v(3)

Finally, by calling find(prod(img == reshape(val, 1, 1, 3), 3)) on the previous result we find the indices of all non-zero elements of the 2d array which match the RGB value.

Note: If you write the following ind = find(prod(img == reshape(val, 1, 1, 3), 3)) instead of [row, col] = find(prod(img == reshape(val, 1, 1, 3), 3)) then a linear index value will be returned instead of two indices that correspond to the pixel's coordinates in the array.

Example

Let's create a random image and assign the question's RGB value at indices $(3,3)$ meaning the 3 row and 3 column pixel

>> img = randi([0, 255], 4, 4, 3); val = [144, 127, 11]; img(3,3,:) = val
img =

ans(:,:,1) =

     2   147    61   139
   239   211    16   103
    84   184   144   238
    32   145   160    78

ans(:,:,2) =

   173    73    72   239
   122    57    39    33
   121     4   127   225
   176   160   127   186

ans(:,:,3) =

   152   110    77    38
   129    41   186    85
   102   211    11   105
    67    82   181   166

Now we can call reshape to see what the output looks like

>> reshape(val, 1, 1, 3)
ans =

ans(:,:,1) =  144
ans(:,:,2) =  127
ans(:,:,3) =  11

the output is a $1\times 1\times 3$ array.

Next, we call the == operator (or eq):

>> img == reshape(val, 1, 1, 3)
ans =

ans(:,:,1) =

  0  0  0  0
  0  0  0  0
  0  0  1  0
  0  0  0  0

ans(:,:,2) =

  0  0  0  0
  0  0  0  0
  0  0  1  0
  0  0  1  0

ans(:,:,3) =

  0  0  0  0
  0  0  0  0
  0  0  1  0
  0  0  0  0

what this step did is to 'mark' with 1 all the elements that matched the value of their corresponding RGB channel value.

Next, we call prod to aggregate this 3d truth array into a 2d truth array as if we would have explicitly performed a logical and operation on them. The second argument given to prod is the dimension along we want the elementwise product to be performed. Since we want to aggregate the each color channel's 2d truth array, we will perform the product along the third dimension. This isn the resulting 2d array

>> prod(img == reshape(val, 1, 1, 3), 3)
ans =

   0   0   0   0
   0   0   0   0
   0   0   1   0
   0   0   0   0

Finally, by calling find, we get the indices

>> [row, col] = find(prod(img == reshape(val, 1, 1, 3), 3))
row =  3
col =  3

Note

If we had just called find without the [row, col] assignment then the returned value would be a linear index

>> find(prod(img == reshape(val, 1, 1, 3), 3))
ans =  11

Tip

For convenience, you could also wrap the line of code into an anonymous function, should you find yourself using it multiple times, like this

IdxByRGB = @(img, val) find(prod(img == reshape(val, 1, 1, 3), 3)) 

and then call the function on whatever image and color value you want

[row, col] = IdxByRGB(img, val) 
$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.