Contrast Adjustment
This example shows how to generate HDL code from a MATLAB® design that adjusts image contrast by linearly scaling pixel values.
Algorithm
Contrast adjustment adjusts the contrast of an image by linearly scaling the pixel values between upper and lower limits. Pixel values that are above or below this range are saturated to the upper or lower limit value, respectively.

MATLAB Design
design_name = 'mlhdlc_image_scale'; testbench_name = 'mlhdlc_image_scale_tb';
Look at the MATLAB design:
type(design_name);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% scale.m
%
% Adjust image contrast by linearly scaling pixel values.
%
% The input pixel value range has 14bits and output pixel value range is
% 8bits.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [x_out, y_out, pixel_out] = ...
mlhdlc_image_scale(x_in, y_in, pixel_in, ...
damping_factor_in, dynamic_range_in, ...
tail_size_in, max_gain_in, ...
width, height)
% Copyright 2011-2022 The MathWorks, Inc.
persistent histogram1 histogram2
persistent low_count
persistent high_count
persistent offset
persistent gain
persistent limits_done
persistent damping_done
persistent reset_hist_done
persistent scaling_done
persistent hist_ind
persistent tail_high
persistent min_hist_damped %Damped lower limit of populated histogram
persistent max_hist_damped %Damped upper limit of populated histogram
persistent found_high
persistent found_low
DR_PER_BIN = 8;
SF = 1./(1:(2^14/8)); % be nice to fix this
NR_OF_BINS = (2^14/DR_PER_BIN) - 1;
MAX_DF = 255;
if isempty(offset)
offset = 1;
gain = 1;
limits_done = 1;
damping_done = 1;
reset_hist_done = 1;
scaling_done = 1;
hist_ind = 1;
tail_high = NR_OF_BINS;
low_count = 0;
high_count = 0;
min_hist_damped = 0;
max_hist_damped = (2^14/DR_PER_BIN) - 1;
found_high = 0;
found_low = 0;
end
if isempty(histogram1)
histogram1 = zeros(1, NR_OF_BINS+1);
histogram2 = zeros(1, NR_OF_BINS+1);
end
if y_in < height
frame_valid = 1;
if x_in < width
line_valid = 1;
else
line_valid = 0;
end
else
frame_valid = 0;
line_valid = 0;
end
% initialize at beginning of frame
if x_in == 0 && y_in == 0
limits_done = 0;
damping_done = 0;
reset_hist_done = 0;
scaling_done = 0;
low_count = 0;
high_count = 0;
hist_ind = 1;
end
max_gain_frac = max_gain_in/2^4;
pix11 = floor(pixel_in/DR_PER_BIN);
pix_out_temp = pixel_in;
%**************************************************************************
%Check if valid part of frame. If pixel is valid remap pixel to desired
%output dynamic range (dynamic_range_in) by subtracting the damped offset
%(min_hist_damped) and applying the calculated gain calculated from the
%previous frame histogram statistics.
%**************************************************************************
% histogram read
histReadIndex1 = 1;
histReadIndex2 = 1;
if frame_valid && line_valid
histReadIndex1 = pix11+1;
histReadIndex2 = pix11+1;
elseif ~limits_done
histReadIndex1 = hist_ind;
histReadIndex2 = NR_OF_BINS - hist_ind;
end
histReadValue1 = histogram1(histReadIndex1);
histReadValue2 = histogram2(histReadIndex2);
histWriteIndex1 = NR_OF_BINS+1;
histWriteIndex2 = NR_OF_BINS+1;
histWriteValue1 = 0;
histWriteValue2 = 0;
if frame_valid
if line_valid
temp_sum = histReadValue1 + 1;
ind = min(pix11+1, NR_OF_BINS);
val = min(temp_sum, tail_size_in);
histWriteIndex1 = ind;
histWriteValue1 = val;
histWriteIndex2 = ind;
histWriteValue2 = val;
%Scale pixel
pix_out_offs_corr = pixel_in - min_hist_damped*DR_PER_BIN;
pix_out_scaled = pix_out_offs_corr * gain;
pix_out_clamp = max(min(dynamic_range_in, pix_out_scaled), 0);
pix_out_temp = pix_out_clamp;
end
else
%**********************************************************************
%Ignore tail_size_in pixels and find lower and upper limits of the
%histogram.
%**********************************************************************
if ~limits_done
if hist_ind == 1
tail_high = NR_OF_BINS-1;
offset = 1;
found_high = 0;
found_low = 0;
end
low_count = low_count + histReadValue1;
hist_ind_high = NR_OF_BINS - hist_ind;
high_count = high_count + histReadValue2;
%Found enough high outliers
if high_count > tail_size_in && ~found_high
tail_high = hist_ind_high;
found_high = 1;
end
%Found enough low outliers
if low_count > tail_size_in && ~found_low
offset = hist_ind;
found_low = 1;
end
hist_ind = hist_ind + 1;
%All bins checked so limits must already be found
if hist_ind >= NR_OF_BINS
hist_ind = 1;
limits_done = 1;
end
%**********************************************************************
%Damp the limit change to avoid image flickering. Code below equivalent
%to: max_hist_damped = damping_factor_in*max_hist_dampedOld +
%(1-damping_factor_in)*max_hist_dampedNew;
%**********************************************************************
elseif ~damping_done
min_hist_weighted_old = damping_factor_in*min_hist_damped;
min_hist_weighted_new = (MAX_DF-damping_factor_in+1)*offset;
min_hist_weighted = (min_hist_weighted_old + ...
min_hist_weighted_new)/256;
min_hist_damped = max(0, min_hist_weighted);
max_hist_weighted_old = damping_factor_in*max_hist_damped;
max_hist_weighted_new = (MAX_DF-damping_factor_in+1)*tail_high;
max_hist_weighted = (max_hist_weighted_old + ...
max_hist_weighted_new)/256;
max_hist_damped = min(NR_OF_BINS, max_hist_weighted);
damping_done = 1;
hist_ind = 1;
%**********************************************************************
%Reset all bins to zero. More than one bin can be reset per function
%call if blanking time is too short.
%**********************************************************************
elseif ~reset_hist_done
histWriteIndex1 = hist_ind;
histWriteValue1 = 0;
histWriteIndex2 = hist_ind;
histWriteValue2 = 0;
hist_ind = hist_ind+1;
if hist_ind == NR_OF_BINS
reset_hist_done = 1;
end
%**********************************************************************
%The gain factor is determined by comparing the measured damped actual
%dynamic range to the desired user specified dynamic range. Input
%dynamic range is measured in bins over DR_PER_BIN space.
%**********************************************************************
elseif ~scaling_done
dr_in = round(max_hist_damped - min_hist_damped);
gain_temp = dynamic_range_in*SF(dr_in);
gain_scaled = gain_temp/DR_PER_BIN;
gain = min(max_gain_frac, gain_scaled);
scaling_done = 1;
hist_ind = 1;
end
end
histogram1(histWriteIndex1) = histWriteValue1;
histogram2(histWriteIndex2) = histWriteValue2;
x_out = x_in;
y_out = y_in;
pixel_out = pix_out_temp;
type(testbench_name);
%Test bench for scaling, analogous to automatic gain control (AGC)
% Copyright 2011-2022 The MathWorks, Inc.
testFile = 'mlhdlc_img_peppers.png';
imgOrig = imread(testFile);
[height, width] = size(imgOrig);
imgOut = zeros(height,width);
hBlank = 20;
% make sure we have enough vertical blanking to filter the histogram
vBlank = ceil(2^14/(width+hBlank));
%df - Temporal damping factor of rescaling
%dr - Desired output dynamic range
df = 0;
dr = 255;
nrOfOutliers = 248;
maxGain = 2*2^4;
for frame = 1:2
disp(['frame: ', num2str(frame)]);
for y_in = 0:height+vBlank-1
%disp(['frame: ', num2str(frame), ' of 2, row: ', num2str(y_in)]);
for x_in = 0:width+hBlank-1
if x_in < width && y_in < height
pixel_in = double(imgOrig(y_in+1, x_in+1));
else
pixel_in = 0;
end
[x_out, y_out, pixel_out] = ...
mlhdlc_image_scale(x_in, y_in, pixel_in, df, dr, ...
nrOfOutliers, maxGain, width, height);
if x_out < width && y_out < height
imgOut(y_out+1,x_out+1) = pixel_out;
end
end
end
figure('Name', [mfilename, '_scale_plot']);
imgOut = round(255*imgOut/max(max(imgOut)));
subplot(2,2,1); imshow(imgOrig, []);
title('Original Image');
subplot(2,2,2); imshow(imgOut, []);
title('Scaled Image');
subplot(2,2,3); histogram(double(imgOrig(:)),2^14-1);
axis([0, 255, 0, 1500]);
title('Histogram of original Image');
subplot(2,2,4); histogram(double(imgOut(:)),2^14-1);
axis([0, 255, 0, 1500]);
title('Histogram of equalized Image');
end
Simulate the Design
It is a good practice to simulate the design with the testbench prior to code generation to make sure there are no runtime errors.
mlhdlc_image_scale_tb
frame: 1 frame: 2


Create a New HDL Coder™ Project
coder -hdlcoder -new mlhdlc_scale_prj
Next, add the file mlhdlc_image_scale.m to the project as the MATLAB Function and mlhdlc_image_scale_tb.m as the MATLAB Test Bench.
Refer to Get Started with MATLAB to HDL Workflow for a more complete tutorial on creating and populating MATLAB HDL Coder projects.
Run Fixed-Point Conversion and HDL Code Generation
Launch the Workflow Advisor from the Workflow Advisor button and right-click on the Code Generation step and choose the option Run to selected task to run all the steps from the beginning through the HDL code generation.
Examine the generated HDL code by clicking on the hyperlinks in the Code Generation Log window.