Dimming an Image with a new max?

1 view (last 30 days)
Ray
Ray on 20 Oct 2014
Edited: DGM on 25 Apr 2023
function [dimmed] = dimImage(orig, new_max)
Write a function called dim Image which takes two inputs: a 3 D matrix representing a color image , and a new maximum brightness . This function should return a 3 D matrix with its value range reduced to fit between 0 and the new maximum brightness ( new_max )
I feel as though I am doing this properly as my code looks like:
dimmed = min(orig, new_max-1)
But when I run a sample image, it is not dimmed, but there is another alteration. Can someone give me direction?

Answers (3)

Image Analyst
Image Analyst on 21 Oct 2014
Try this:
function [dimmed] = dimImage(grayImage, new_max)
% Clip to the value they specify
pixelsToClip = grayImage > new_max;
grayImage(pixelsToClip) = new_max;
dimmed = grayImage;
A full blown demo is attached below the following image, which it will create.
%

DGM
DGM on 2 Dec 2022
Edited: DGM on 25 Apr 2023
The intent seems terribly obvious to me. OP wants an image "reduced to a new maximum", and data truncation is specifically described as an example of what they don't want because it "is not dimmed" but instead affected in other negative ways.
This is a basic levels adjustment. It can be done with imadjust() or basic arithmetic to effect linear rescaling. My goal here is to demonstrate the difference in effect on the image, not to write a function demo for a homework assignment.
% read an image (RGB,uint8)
inpict = imread('peppers.png');
% new output white value
wvout = 0.7;
% truncate image to fit within [0 wvout]*255
% this is what both prior examples do
outpictnonlin = min(inpict,wvout*255);
% linearly rescale image to fit within [0 wvout]*255
outpictlin = imadjust(inpict,[0 1],[0 wvout]);
% compare
imshow([inpict; outpictnonlin; outpictlin])
Obviously, truncation is conspicuously destructive to image regions which are above the threshold. Avoid it unless your goal is to discard information. Image regions below the threshold aren't altered.
The rescaled image is dimmer overall. Brightness and global contrast are reduced, but details are preserved.
Bear in mind that this rescaling is done with respect to the nominal data range, and is independent of the actual extrema of the input image. In other words, the new global maximum would only be the specified value if it were maximized (e.g. 255 for uint8) prior to the adjustment. This is the typical behavior for simple image adjustment tools. If the goal were to scale the image such that its original extrema correspond to [0 wvout], then the second argument to imadjust() should be those extrema, represented in unit-scale. One can use stretchlim() to obtain those values.
Now that that's done, we're back to the assignment. There are multiple copies of this assignment that have been posted. While it's clear that it's a basic scaling, I still haven't found one that unambiguously describes the intended behavior regarding the input limits. Since nobody can communicate what they want, nobody will get what they want. Here is a collage of code that should be sufficient to construct all that I assume might be required.
% an image
inpict = imread('peppers.png');
% cast to floating point
% scale to some standard scale (i.e. [0 1])
% that way the code works regardless of input class
inpict = im2double(inpict);
% it's up to you to define levels and choose _how_ and _where_ they get defined
inrange = [0 1];
outrange = [0 1];
% rescale the image data
outpict = (inpict-inrange(1))./range(inrange); % adjust input levels
outpict = outpict.*range(outrange) + outrange(1); % adjust output levels
% boldly presume that the output should always be uint8
outpict = im2uint8(outpict);
If the goal is to scale the input to the original extrema as discussed, then inrange needs to be calculated somehow. For a two-term result, you could do:
% you could take the naive approach
inrange = [min(inpict(:)) max(inpict(:))]; % find extrema
% or if you want to emulate stretchlim(), it gets more complicated.
For a 6-term result, you could do the following, but it would obviously require changes to the math above.
% if you want the input levels to be independent per channel:
inrange = permute([min(inpict,[],[1 2]) max(inpict,[],[1 2])],[2 3 1]);

Image Analyst
Image Analyst on 25 Apr 2023
Try
dimmed = rescale(orig, new_min, new_max);
For example
dimmed = rescale(orig, 0, 157); % Linearly scale image to now go between 0 and 157 gray levels.
  1 Comment
DGM
DGM on 25 Apr 2023
Edited: DGM on 25 Apr 2023
The output from rescale() is floating point, so presuming that the output is uint8-scale would also require the output to be cast accordingly (at least eventually).
inpict = imread('pout.tif');
% presume that the output should always be uint8
dimmed = rescale(inpict, 0, 157);
dimmed = uint8(dimmed);
This does bring up the issue of the intended behavior with color images as OP had. Bear in mind that the simple 2-parameter syntax for rescale() only specifies the output levels. The default input levels are the global extrema of the entire array. Consider the differences between this behavior and that of
dimmed = imadjust(inpict,stretchlim(inpict,0),[outlo outhi]);
when inpict is an RGB image. Should channels be scaled independently, or should the entire array be scaled? Depending on how literal we choose to read the question, perhaps we should be scaling the HSV value. Let's observe.
% create a low-contrast color image with different ranges per channel
inpict = imread('pout.tif');
inpict = cat(3,inpict,inpict*0.8,inpict*0.6);
stretchlim(inpict,0) % these are the channel extrema in unit-scale
ans = 2×3
0.2902 0.2314 0.1725 0.8784 0.7020 0.5255
% specified output levels
outlevels = [20 157];
% input levels are extrema across all channels
dimmedI = rescale(inpict,outlevels(1),outlevels(2));
dimmedI = uint8(dimmedI);
% input levels are extrema of each channel
inlevels = stretchlim(inpict,0);
dimmedRGB = imadjust(inpict,inlevels,outlevels/255);
% input levels are the maximum of channel extrema (the extrema of V)
hsvpict = rgb2hsv(inpict);
hsvpict(:,:,3) = rescale(hsvpict(:,:,3),outlevels(1)/255,outlevels(2)/255);
dimmedHSV = im2uint8(hsv2rgb(hsvpict));
imshow([inpict dimmedI dimmedRGB dimmedHSV])
outlevels/255 % the specified output extrema in unit-scale
ans = 1×2
0.0784 0.6157
stretchlim(dimmedI,0) % outlevels controls global extrema
ans = 2×3
0.1686 0.1216 0.0784 0.6157 0.4824 0.3490
stretchlim(dimmedRGB,0) % outlevels controls channel extrema
ans = 2×3
0.0784 0.0784 0.0784 0.6157 0.6157 0.6157
stretchlim(dimmedHSV,0) % outlevels controls maximum of channel extrema
ans = 2×3
0.0784 0.0627 0.0471 0.6157 0.4902 0.3686
As always, it depends what the goals are.

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!