How do I superimpose images using imfuse() while keeping colors?

6 views (last 30 days)
Joon-Ho Hwang
Joon-Ho Hwang on 16 Feb 2016
Edited: DGM on 4 Jun 2022
I have to use imfuse() function since I want to set the coordinate of the image that will be applied (Of course it's smaller than background)
So... this is the code:
a = imresize(imread('Color/1.png'), [300 NaN], 'nearest');
b = imread('Base.png');
ra = imref2d(size(a));
rb = imref2d(size(b));
ra.XWorldLimits = ra.XWorldLimits + 20;
ra.YWorldLimits = ra.YWorldLimits + 20;
c = imfuse(b, rb, a, ra);
imshow(c)
This is Base.png(It's rgb, not binary nor gray):
And this is 1.png:
And... this is the result:
I used all options like 'blend', 'diff', 'Scaling' and 'ColorChannels'...
But I failed...
'blend' option is the nearest to what I want, but it is still darkened

Answers (1)

DGM
DGM on 4 Jun 2022
Edited: DGM on 4 Jun 2022
The short answer is very simple. Don't use imfuse() for image composition. Imfuse() is a tool for visually comparing two images. It's not a practical image composition or blending tool.
I'm going to ignore the usage of imref2d() here. Can you use those tools to get the subscripts of the offset FG image within the coordinate space of the BG? Probably, but don't ask me how. Will the coordinates be valid if the offset puts the FG content outside the boundary of the BG? No, so you'll have to write around that. I fail to see that these referencing tools actually reduce the complexity of the task.
If the goal is to merely produce a temporary visualization of the two images, then you can just stack the images in a figure using imshow() and specifying the 'xdata' and 'ydata' properties when drawing the FG object.
I'm going to assume that the goal is to produce an image -- an actual raster image array that can be saved. In that case, drawing to a figure and saving a screenshot would be a bad idea. Let's start with a simple approach based on direct indexing. This is fast and requires no special tools, but it has limitations. For sake of clarity, I'm going to modify the BG so that the extent of the FG is visible against the BG, and so that the extent of the BG is visible against this awful white webpage.
FG = imread('color1.png');
BG = imread('baseclouds.png');
% resize FG
FG = imresize(FG,[300 NaN],'nearest');
offset = [21 50]; % [y x];
% calculate coordinates of offset image
szf = size(FG);
szb = size(BG);
offset = min(max(offset,0),szb(1:2)-szf(1:2));
xidx = offset(2)+1:offset(2)+szf(2);
yidx = offset(1)+1:offset(1)+szf(1);
% insert FG by direct indexing
% this assumes that both images are the same class
% and have the same number of color channels
outpict = BG;
outpict(yidx,xidx,:) = FG;
imshow(outpict)
Note that the FG is prevented from being positioned outside the extent of the BG.
While the above is simple, it's not flexible. It assumes that the image data can be directly combined -- that requires them to have the same numeric class (and scaling) and the same number of color channels. You could conditionally expand/collapse either image based on the number of color channels, and you could check the class of the BG and cast (and rescale) the FG to match.
A more generalized approach start with using MIMT's composition and blending tools to combine the FG into the BG region. While it's not necessary for solid images, it allows for transparent images, or the use of scalar opacity adjustment. Using imblend() here means that the process is no longer dependent on the FG and BG having the same class. Since the full image isn't being composed with imblend(), it's still important that the result of imblend() has the same number of color channels as BG. Imblend() will automatically expand images when compositing/blending, so as long as BG has at least as many color channels as FG, it will work without explicit expansion of either image.
FG = imread('color1.png');
BG = imread('baseclouds.png');
% resize FG
FG = imresize(FG,[300 NaN],'nearest');
% get rid of FG matting so it has more room to move
% FG is now an RGBA image
mk = all(FG==0,3);
mk = ~bwareafilt(mk,1,4);
FG = joinalpha(FG,mk);
%imshow2(FG) % FG has transparency now
offset = [21 50]; % [y x];
% calculate coordinates of offset image
szf = size(FG);
szb = size(BG);
offset = min(max(offset,0),szb(1:2)-szf(1:2));
xidx = offset(2)+1:offset(2)+szf(2);
yidx = offset(1)+1:offset(1)+szf(1);
% extract region from BG, compose with FG, insert back into BG
% imblend() does not depend on class/channels to match
outpict = BG;
bgroi = outpict(yidx,xidx,:); % extract ROI from BG
bgroi = imblend(FG,bgroi,1,'normal'); % compose with FG
bgroi = splitalpha(bgroi); % strip extraneous alpha channel
outpict(yidx,xidx,:) = bgroi; % insert into BG
imshow(outpict)
Note that by adding alpha to FG, there is more room to move it without it interfering with the BG border. Also note that the FG opacity can be adjusted with imblend.
bgroi = imblend(FG,bgroi,0.3,'normal'); % compose with FG
Of course, if there are only a couple images and code simplicity is more important than computational efficiency, the same can be done using imstacker(). In this case, the composition is performed on the entire image area, so there are no longer any edge cases to consider regarding matching the number of color channels.
FG = imread('color1.png');
BG = imread('baseclouds.png');
% resize FG
FG = imresize(FG,[300 NaN],'nearest');
% get rid of FG matting so it has more room to move
% FG is now an RGBA image
mk = all(FG==0,3);
mk = ~bwareafilt(mk,1,4);
FG = joinalpha(FG,mk);
%imshow2(FG) % FG has transparency now
offset = [0 50]; % [y x];
% stack the images within a working space defined by the largest image (default behavior)
% pad smaller images using a transparent black matting (default behavior)
% use "west" gravity; this aligns the images vertically, so no extra y-offset is needed for FG
outpict = imstacker({FG; BG},'offset',[offset; 0 0],'gravity','w');
% merge the stack using basic src-over composition
outpict = mergedown(outpict,1,'normal');
% since the base image is opaque, we can strip the extraneous alpha channel
% if the base image were not opaque, we would want to keep it
outpict = splitalpha(outpict);
imshow(outpict)
While the FG is shown with full opacity in this example, the opacity can be adjusted in the call to mergedown(), just as it was in the prior example using imblend().
Note also that the specified gravity vertically centers the two images, so specifying a vertical offset is not necessary to get the images centered on that axis. Also, the way imstacker() handles the offsets allows the FG to be placed outside the extents of the BG without causing problems. The FG is simply truncated if it extends outside the stack area.
offset = [100 -100]; % [y x];
This further demonstrates the benefit of adding transparency to the FG image
Back to what I mentioned about the first example. How does one (generally) cast and rescale an image to match the class of another image? Well, if you're using IPT, you can make some awful garbage like this:
outclass = class(BG);
switch outclass
case 'uint8'
FG = im2uint8(FG);
case 'double'
FG = im2double(FG);
case 'single'
FG = im2single(FG);
case 'uint16'
FG = im2uint16(FG);
case 'int16'
FG = im2int16(FG);
case 'logical'
FG = logical(FG); % this is a great way to create problems
otherwise
error('kablooey')
end
... because the primary parameter (the class name) is embedded in the function names. It's almost like you're being tempted to use eval() or something.
You could also use cast(), which does support the class name as a parameter, but then you'll have to take care to correctly scale the data, because cast() doesn't scale anything.
... or you can just use MIMT imcast():
FG = imcast(FG,class(BG));

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!