Fading/shading an image
Afficher commentaires plus anciens
Good evening,
I had to darken half of a RGB image in order to recreate an emianopsy. Here is the code I used:
%Loss of left visus
A = imread('lena .bmp');
[x,y,z]=size(A); %dimensions' estraction RGB image
Y=y/2; % halving y axis
HSV = rgb2hsv(A); %conversion to hsv space
%Process the HSV image. This example decrease the brightness of half image by multiplying the V channel by a scale factor.
[h,s,v] = imsplit(HSV); %split hue, saturation and value channels
vFactor = 0.2; %define the scale factor for brightness
% for cicle darken pixels of the left part of the image
for i=1:x
for j=1:Y
v(i,j) = v(i,j)*vFactor;
end
end
HSV_v = cat(3,h,s,v); %reconstrucion of HSV image
%Convert the processed HSV image back to the RGB color space.
Av = hsv2rgb(HSV_v);
figure
imshow(Av)
title('Left emianopsy');
Here is the resulting image:

The separation between the 2 parts of the image is too sharp and marked, so I'd like to obtain a more faded transition between the dark and the coloured side (like a gradient?). How can I obtain a realistic result? like for example the image below:

Thank you in advance
1 commentaire
hosein Javan
le 11 Août 2020
the reason why your image is not a garadient, is that you are multiplying one single value factor to the whole half left part of the image rather than multiplying a series of factors that change smoothly according to pixel position.
Réponse acceptée
Plus de réponses (1)
A 'multiply' blend is one of the simple and fundamental kinds of image blending. If you have appropriate images to begin with and you pay attention to your image class and data scaling, you should be fine.
I've posted plenty of other reference answers about doing similar things with standard tools, but I don't feel like it tonight. MATLAB does not have any practical tools for image blending or composition, so I think it's kind of unreasonable to reinvent a missing wheel every single time. For these examples, I'm going to use tools from MIMT, which is available on the File Exchange.
As in any image manipulation environment, MIMT imblend() can simply blend (and composite) two images. You don't have to be stuck with ultra-simple things like 'multiply' and 'addition' blends, and you don't have to guess at the behavior of closed-source software which no longer exists or debug potentially erroneous formulae found on an abandoned blog somewhere. I already did that.
% background image (RGB, uint8)
BG = imread('lena.jpg');
% create foreground image (grayscale, uint8)
s = imsize(BG);
FG = lingrad(s(1:2),[0.2 0.25; 0.8 0.75],[0; 255],'cosine');
% show the images
montage({FG,BG})

% blend the images
%outpict = imblend(FG,BG,1,'multiply');
%outpict = imblend(FG,BG,1,'colorburn',0.5);
%outpict = imblend(FG,BG,1,'linearburn',0.5);
outpict = imblend(FG,BG,1,'suauburn');


While imblend() is compared to Photoshop or GIMP in that it supports image blending, it's dissimilar in that it supports more options than any image manipulation suite does. Most of the modes are adjustable in ways which are convenient for their typical use. In the above example, the lineardodge and colordodge blends are done with a parameter of 0.5, resulting in subtle and rich results unobtainable through scalar opacity adjustment.
Admittedly, my goal here isn't to create anything that really looks good. Slapping a black gradient on lena is just a tangent on the replication of OP's example. Anyone who is familiar with blending images in Photoshop, etc should be able to plan the composition they desire. Having a better hammer doesn't help if you don't know where to put the nails.
So let's try another. MIMT also has tools for creating simple two-point and multipoint gradients. Instead of a linear grayscale gradient, let's use a colored radial gradient. We'll use the same sort of NRL-leveraged blending with darkening modes to do a sort of goofy mood vignetting.
% background image (RGB, uint8)
BG = imread('lena.jpg');
% create foreground image (RGB, uint8)
easew = 0.5; % normalized WRT image width
s = imsize(BG);
FG = radgrad(s(1:3),[0.5 0.5],0.5,[255 255 255; 100 50 255],'ease');
% show the images
montage({FG,BG})

% blend the images
%outpict = imblend(FG,BG,1,'multiply');
%outpict = imblend(FG,BG,1,'colorburn',0.8);
%outpict = imblend(FG,BG,1,'linearburn',0.8);
outpict = imblend(FG,BG,1,'suauburn');


I suppose that's a bit better than black. Again, these blends are adjustable. It takes more than a handful of pictures to explain what the difference is between 'multiply' and the other modes. If all you need to do is to something like this, the details probably don't matter much. The synopsis for imblend() has extensive description of the behaviors, and most modes have their technical properties documented here.
As for the last questions about blurring an image region -- this is a simple task of linear image composition. You could combine images using a logical mask, but the transition will always have ugly jagged edges. You want to do linear composition.
Again, much like the black gradient overlay, you can't get a smooth transition if you don't start with a smooth mask. Whether it's a circle or a polygon, you'll need to come up with a way to make it smooth. If you start with a logical image and try to blur it, make sure to cast it as something other than logical before you blur it.
Don't use roifilt() if appearances are important. Internally, roifilt() is very simple in concept. Much like the code below, it filters a copy of the image and then composes the two copies of that image. Instead of doing that to the whole image, it does it on the rectangular region which circumscribes the mask ROI. It doesn't do any sort of fancy mask-aware weighting or window traversal to process the actual nonrectangular mask region. It's useful for its efficiency when the filtered region is very small in comparison to the overall image, but it uses logical composition internally, so the results will always have jagged edges, even if the mask is smooth.
While roifilt2() is limited like that, MIMT has a similar tool called roifilter() that can support linear masks, color images, and blobwise operations. If the mask region is small compared to the image, there may be a speed advantage to using it. In this example, a soft circular mask is simply generated by applying a nonlinear contrast curve to a radial gradient with a linear ease curve. A blur operation is confined to the region defined by the mask.
% background image (RGB, uint8)
BG = imread('lena.jpg');
% create soft mask
mask = radgrad(imsize(BG,2),[0.5 0.5],0.27,[255; 0]); % linear radial gradient
mask = imlnc(mask,'independent','in',[0 0.5],'k',3); % adjust contrast
% use roifilter() with a kernel size spec
% this specifies the diameter of a gaussian kernel with sigma = 0.15*D
outpict = roifilter(BG,mask,133,'linear'); % sigma = ~20px

% use roifilter() by supplying a filter kernel instead
fk = fspecial('gaussian',133,20);
outpict = roifilter(BG,mask,fk,'linear');

If the mask area isn't significantly smaller than the image or there's other needs, this can always be done by basic composition as before. While imblend() can be used for both blending and compositing, replacepixels() is often more convenient when all that's needed is compositing (and when the mask is unattached). It's definitely more convenient than doing all the class handling and multiplication the long way every time.
% background image (RGB, uint8)
BG = imread('lena.jpg');
% create foreground image (RGB, uint8)
FG = imgaussfilt(BG,20);
% create soft mask
mask = radgrad(imsize(FG,2),[0.5 0.5],0.27,[255; 0]); % linear radial gradient
mask = imlnc(mask,'independent','in',[0 0.5],'k',3); % adjust contrast
% show the images and mask
montage({FG,BG,mask})

% compose the images in linear RGB
outpict = replacepixels(FG,BG,mask,'linear');

While something like this is easily handled by composition in practice, be aware that transitioning a blur by composition isn't the same thing as using a variable-kernel blurring operation. See this answer:
Consider also the following answers to similar questions regarding basic image composition:
5 commentaires
dinesh bharathi
le 21 Sep 2022
Thanks for the explanation, this is very useful. I want to create a Gradient but not so smooth, I was to reduce the edge softness as preferred, that way the fading happens in little less pixels. I tried adjusting the parameters in lingrad but that doesn't seem to work as i desired. Can you help me on this!?
Image Analyst
le 21 Sep 2022
Do you want the linear version or the circular version?
I'm assuming you're after the gradient in the first example -- because the easewidth adjustment in the code as originally posted was actually broken. That's what I get for reusing code without double checking.
The spec for both lingrad() and radgrad() are similar, so this may help either way. I might as well elaborate for other passers-by in the future.
From the lingrad() synopsis:
% POINTS is a 2x2 matrix specifying gradient endpoints
% [y0 x0; y1 x1] (normalized coordinates)
So much like one would do by dragging with a mouse when creating a gradient in an image editor, your goal is to create a line segment by defining two points. The following example should work a little more like I'd intended. It still assumes that the gradient is just slapped along the diagonal of a square image. These are the points that are being used:

If the image isn't square, or if you want it positioned elsewhere or at a specific angle, you'd need to do the extra math. The main point is just to illustrate that the only thing changing here is the distance between the two points.
% create foreground image (grayscale, uint8)
szo = [200 200]; % pixels
%easew = 0.2; % normalized WRT image width
%easew = 0.4; % normalized WRT image width
easew = 0.6; % normalized WRT image width
FG = lingrad(szo,[1-easew 0; 1 easew],[0; 255],'cosine');

Bear in mind that changing the shape of the ease curve influences the perceived width as well. That said, piecewise-linear gradients tend to create disagreeable contrast effects at their endpoints.
szo = [200 200]; % pixels
easew = 0.4; % normalized WRT image width
%FG = lingrad(szo,[1-easew 0; 1 easew],[0; 255],'linear');
%FG = lingrad(szo,[1-easew 0; 1 easew],[0; 255],'softease');
FG = lingrad(szo,[1-easew 0; 1 easew],[0; 255],'cosine');

The fact that these are all specified in normalized coordinates can be confusing and cumbersome when the needs get particular, but most (especially early) MIMT tools were made for ad-hoc nontechnical image editing. That sort of usage favors the convenience that normalized coordinates provide.
That said, here is a more elaborate example. Instead of simply picking endpoints by subdividing the edges (which only really works intuitively on a square), this allows the specification of a gradient centered at any location and inclined at any particular angle.
% create foreground image (grayscale, uint8)
szo = [200 500]; % pixels
easew = 0.4; % normalized WRT image width
theta = 30; % degrees
center = [0.5 0.5]; % [y x], normalized
hyp = 0.5*easew*norm(szo(1:2))*[cosd(theta) sind(theta)]; % denormalized
pos = szo(1:2).*center + [-hyp; hyp]; % denormalized
FG = lingrad(szo,pos./szo(1:2),[0; 255],'cosine');
% show the gradient image
imshow(FG); hold on
% show where the breakpoints are in this case
plot(pos(:,2),pos(:,1),':*','markersize',20,'linewidth',2)

Because the required coordinates are normalized and we're dealing with offsets, it's easiest to just calculate the endpoint locations in pixels and then normalize them at the end.
In this example, I chose theta to represent the angle of the gradient front as opposed to the line of descent. Switching that is simple enough.
As in the prior example, when easew is 1, the distance between endpoints is equal to the length of the image diagonal. Again, I prefer thinking about these things with respect to gross image geometry. There's no reason that can't be done differently.
If you're using MIMT there's always the possibility of using imgenerate(), as it provides a graphical interface for creating images via lingrad/radgrad. The points can simply be chosen with the mouse (it's a bit clumsy, but not terrible). The colors can be chosen with a color selection dialog if desired. It can't do everything that lingrad/radgrad can do on their own, but it covers most functionality.
dinesh bharathi
le 23 Sep 2022
Meanwhile apart from the linear and radial, is it also possible to have complex shapes? like uneven rectangle or polygon or something similar to that?
There isn't really a MIMT tool specifically for arbitrary gradients. There are some options though. Consider the following example:
% say you have a mask that defines the shape
% this is a single-channel antialiased image
mask = imread('monojagblob.png');

% invert the mask, then calculate the distance transform, normalize
% this creates a unit-scale map from the object boundary to its interior
D = simnorm(bwdist(iminv(mask)));

Bear in mind that as this example exploits the distance transform, the ease curve of the gradient will be linear. It's possible to adjust that by manipulating the contrast of D.
% the contrast parameter used by imlnc() is a smooth symmetric curve
D = imlnc(D,'independent','k',1.5); % k is a contrast factor

If you want an actual cosine ease curve instead of some arbitrary adjustable thing, that should be easy enough to do mathematically given that D is unit-scale and floating point already.
D = 1-(cos(D*pi)+1)/2; % strictly cosine
If something other than a monochrome gradient is desired, then the colored gradient can be constructed from solid fields using the distance map as a mask.
% for replacepixels(), FG, BG, and the mask
% can individually be either images or color tuples,
% but at least one must be an image.
% in this case, FG and BG are tuples, and D is an image.
CT = uint8([282 183 35; 100 0 80]); % a two-color color table
outpict = replacepixels(CT(1,:),CT(2,:),D);

% use the original mask to constrain the gradient extents if desired
outpict = replacepixels(0,outpict,iminv(mask));

While the composition approach above allows the distance map to be converted into a 2-color gradient, an alternative approach might be to use colormapping tools. That can allow however many colors/breakpoints you can cram into the colormap.
% apply a full colormap instead of a 2-point gradient
numcolors = 256;
CT = ccmap(numcolors); % any colormap of appropriate length
% a non-MIMT approach:
%D = round(D*(numcolors-1) + 1); % for FP classes, idx = 1 corresponds to the first color
%outpict = ind2rgb(D,CT);
% a MIMT approach:
outpict = gray2pcolor(D,CT); % D doesn't even need to be normalized here
% same as before; optional
outpict = replacepixels(0,outpict,iminv(mask));

Catégories
En savoir plus sur Computer Vision with Simulink dans Centre d'aide et File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!






