0

I have two images, one grayscale (Img1Gray) and one coloured (Img2RGB). The coloured image is mostly zero, with several bright coloured regions. I try to superimpose the coloured image on top of the grayscale image, such that the resulting image would look like the grayscale image everywhere where there are no coloured regions, and like the coloured image everywhere where there are. Note that I require the result as an RGB matrix. I have found a solution here

https://stackoverflow.com/a/21070766/3906713

Img1Gray = double(Img1Gray) ./ 255;
Img2RGB  = double(Img2RGB)  ./ 255;

ImgRez = Img2RGB;
ImgRez(:,:,1) = (1 - Img2RGB(:,:,1)) .* Img1Gray + Img2RGB(:,:,1);
ImgRez(:,:,2) = (1 - Img2RGB(:,:,2)) .* Img1Gray + Img2RGB(:,:,2);
ImgRez(:,:,3) = (1 - Img2RGB(:,:,3)) .* Img1Gray + Img2RGB(:,:,3);

imshow(ImgRes);

The code seems to work, however the coloured regions appear significantly more pale than in the original image. How can this be corrected?

Aleksejs Fomins
  • 688
  • 1
  • 8
  • 20

2 Answers2

2

Get the indices of the coloured image where it is non-zero, place these values into the grayscale image.

% Check if any of R/G/B components are non-zero
ColouredImageIdx = any( (Im2RGB > 0), 3 );
% Inidices
[idx(:,1), idx(:,2)] = ind2sub( size(ColouredImageIdx), find(ColouredImageIdx) );

% Create RGB image from grayscale image. Start by making grayscale image 3 layers
OutputImage = repmat(Im1Gray, 1, 1, 3);
% Place colours
OutputImage( idx(:,1), idx(:,2), : ) = Im2RGB( idx(:,1), idx(:,2), : );  

The colours were appearing washed out in your existing code because you are combining the RGB value with some proportion of the grayscale value (proportional to 1-RGB value).

Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • The colors seem to be fine, but there are black boxes around every colored region, and also black boxes for every permutation of region coordinates, resulting in a grid of black boxes – Aleksejs Fomins Feb 28 '18 at 14:42
  • 1
    Without your images to test, and the few lines of code you're using to import your images, I can't reproduce that issue... Please edit these things into your question. – Wolfie Feb 28 '18 at 14:45
  • I'm sorry, I did not put the original images because I am not allowed to. Next time I'll try to generate minimal examples, I was too lazy – Aleksejs Fomins Feb 28 '18 at 15:25
  • 1
    If you ask lazy questions, you'll get less complete answers. Help us help you! Note I've edited my code to use `repmat` instead of your `cat` suggestion. – Wolfie Feb 28 '18 at 15:38
  • I fully agree with what you say. Still, I think it requires experience to determine how much to commit to asking the question. In this particular case I have made a guess that the code alone will be sufficient. I am also still learning to ask – Aleksejs Fomins Mar 01 '18 at 16:53
2

Suppose RGB has value (0.5, 0, 0) on a point A and greyscale has value 0.5 there. The other point B we are interested in has RGB (0.5, 0, 0) and greyscale 0.75, and finally C has (0.75, 0, 0) and 0.5 greyscale. These points are chosen to illustrate the difference between approaches.

0.) Your solution would see A = (0.75, 0.5, 0.5), B = (0.88, 0.75, 0.75) and C = (0.88, 0.5, 0.5). All points would be much lighter shades of red = not what you want.

1.) One solution would be to just set greyscale where there is no RGB image:

someRGB = any(Img2RGB > 0, 3);
ImGrayNoRGB = Img1Gray;
ImGrayNoRGB(someRGB) = 0;
ImgRez = Img2RGB;
ImgRez(:,:,1) = (1 - Img2RGB(:,:,1)) .* ImGrayNoRGB + Img2RGB(:,:,1);
ImgRez(:,:,2) = (1 - Img2RGB(:,:,2)) .* ImGrayNoRGB + Img2RGB(:,:,2);
ImgRez(:,:,3) = (1 - Img2RGB(:,:,3)) .* ImGrayNoRGB + Img2RGB(:,:,3);

Last 4 lines could be simplified to (same option exists in your v0):

ImgRez = (1 - Img2RGB) .* repmat(ImGrayNoRGB, [1, 1, 3]) + Img2RGB;

This is a good solution if RGB is obtained from the same greyscale image and kept color just if it is above threshold, but it will produce poor results for mixing 2 arbitrary images, where greyscale is brighter than the RGB. A = (0.5, 0, 0) is OK and C = (0.75, 0, 0) is fine too, but B = (0.5, 0, 0) is now darker than the supposed greyscale value and this might look bad.

1b) A fix for this "weird looking stuff" would be that luminance of RGB is checked, either by the proper formula (about 0.2R + 0.7G + 0.1B) or simply checking which value is the highest => max(Img2RGB, [], 3). Greyscale is used to set minimum pixel brightness to that value, additively. Assuming max, points would be A = (0.5, 0, 0), B = (0.75, 0.25, 0.25) and C = (0.75, 0, 0).

RGBbright = max(Img2RGB,[], 3); % Get brightness of the RGB image.
deltaBright = greyscale - RGBbright; % Get how much greyscale needs to be added to the RGB.
deltaBright(deltaBright < 0) = 0;
ImgRez = Img2RGB + repmat(deltaBright, [1, 1, 3]);

2.) Another possible solution would be to slightly modify the color mixing in the code, so greyscale is adding white color uniformly and preserving the differences - check maximum RGB and add RGB just in complement to that:

ImgRez = (1 - max(Img2RGB,[], 3)) .* repmat(Img1Gray, [1, 1, 3]) + Img2RGB;

In this case, points are A = (0.75, 0.25, 0.25), B = (0.88, 0.38, 0.38) and C = (0.88, 0.13, 0.13).

Now, for the choice between 1b) and 2), hard to say what to pick. Try both. 2 will make colors a little bit duller and it will additionally have abrupt jumps in image brightness - suppose greyscale image is uniform 0.5 and red region varies between 0 and 0.5 - parts that are red will be brighter than the parts that are grey. So it seems 1b should be a better option. On the other hand, brighter image where there are colors might be desirable outcome of this mixing, favoring 2.

Zizy Archer
  • 1,392
  • 7
  • 11