Why does imhist() do this? (2024)

102 views (last 30 days)

Show older comments

DGM on 18 May 2024 at 11:04

  • Link

    Direct link to this question

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this

  • Link

    Direct link to this question

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this

Edited: DGM on 24 May 2024 at 10:03

Open in MATLAB Online

I thought I had asked this once before, but maybe it was a fever dream. It's hard to tell at this point.

IPT imhist() is a convenience tool for creating histograms of grayscale image data. It bins the image data such that the end bins are centered on the ends of the interval implied by the numeric class of the data (e.g. [0 1] for 'double'). It displays the histogram using a stem() plot, with one stem in the center of each histogram bin.

This much might be disagreeable, since the end bins are effectively half-width, but let's accept the choice to align the bin centers to the interval limits instead of aligning the bin edges.

What I can't understand is the colorbar. Beneath the stem plot is a grayscale colorbar showing the progression N gray levels corresponding to the N histogram bins. The problem is twofold:

  1. While the histogram bin centers are aligned to the interval limits (and cannot be changed), the gray segments of the colorbar have their edges aligned with the interval limits -- and they can't be changed either. The two are always misaligned.
  2. The actual gray values used in the colorbar correspond to the upper edge of where the histogram bins would be if they were edge-aligned, but they're not. The first half of the gray segments don't even correspond to the bin they represent. The asymmetry makes plots with small N extra nonsensical.

So I put together a thing for visual emphasis and figured I'd run it here to see if it's just my old version. It's not.

% some inputs

inpict = rand(500);

n = 5;

% imhist() can either give outputs or plot.

% it can't do both, so we have to call it twice.

imhist(inpict,n); hold on

[counts centers] = imhist(inpict,n);

% find the axes since it won't give them to us

hax = findobj(get(gcf,'children'),'type','axes');

% figure out the bin edges from the centers,

% since it won't give us edges either

dx = diff(centers(1:2));

xr = [centers(1)-dx/2 centers(end)+dx/2];

yr = ylim(hax(2));

% create two images:

% top is a smooth sweep from black to white.

% bottom corresponds to the center of each histogram bin.

% the two images should periodically match at each bin center.

smoothramp = repmat(linspace(xr(1),xr(2),100),[1 1 3]);

binramp = repmat(centers.',[1 1 3]);

binramp = imresize(binramp,[1 size(smoothramp,2)],'nearest');

% concatenate the two stripe images

% clamp as necessary for legacy versions

compramp = min(max([binramp; smoothramp],0),1);

% put the composite image behind the stem plot

hi = image(xr,yr,compramp,'parent',hax(2));

uistack(hi,'bottom')

% find the stem plot and make it fat so it's easier to see

hst = findobj(hax(2),'type','stem');

set(hst,'linewidth',3)

% draw a solid gray circle above each stem,

% such that the circle color is taken directly from the stem position

for k = 1:n

hp = plot(hax(2),centers(k),yr(2)*0.67,'.');

set(hp,'color',[1 1 1]*centers(k));

set(hp,'markersize',60);

end

Why does imhist() do this? (2)

So we have a stem plot, two images, and circular plot markers that all agree, but the color bar is off doing its own thing. The gray level in the first two colorbar segments isn't even in the corresponding histogram bin.

Apparently this is the way imhist() has done it for at least the last 15 years, so is there actually a reason for it, or is it just one of those forever-bugs?

I'm in the middle of trying to write my way around MIMT's usage of imhist(), and I'm inclined to just take a step back and make a complete replacement instead.

3 Comments

Show 1 older commentHide 1 older comment

Stephen23 on 18 May 2024 at 12:22

Direct link to this comment

https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3164841

  • Link

    Direct link to this comment

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3164841

Edited: Stephen23 on 18 May 2024 at 12:58

"so is there actually a reason for it"

Best guess: lack of a precise specification thirty years ago which clearly defined what the function should do AND implementation by an enthusiastic programmer who was not actually an expert in that field AND lack of corner-case testing (e.g. one bin, two bins, three bins) corresponding to those non-existent specifications AND the focus of TMW on supporting average users (who are simply delighted to get any output for their efforts, therefore no bug reports) AND a lack of motivation and resources to correct behaviors that are considered "minor".

Report the bug. Then write your own.

PS: many thanks for the deep dive into IMREAD here:

https://www.mathworks.com/matlabcentral/answers/654298-nonsensical-imread-behavior-with-multiframe-gifs-in-r2019b

DGM on 19 May 2024 at 0:32

Direct link to this comment

https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3165111

  • Link

    Direct link to this comment

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3165111

Open in MATLAB Online

  • indexedblobs.png

Well I suppose I'm setting myself up for disappointment when I ask "why" things were written a certain way.

I was silently expecting that someone might point out a reason that had to do with indexed color support. I hadn't really thought it fully through, but maybe that would explain the motivation to center histogram bins (e.g. on integer values), and it might explain the reason to compromise on colorbar segment alignment (i.e. so that each segment is fully visible). The wrong (1:N)/N gray levels could then just be a mistake made in making scaled color images work with a tool that was originally made with focus on indexed color.

I hadn't actually tested that, since I had no intention of using imhist() for indexed color. So I did, and found out that it's broken in different ways.

% this image has 11 colored blobs on a black background (12 colors)

[Xuint CT] = imread('indexedblobs.png');

imshow(Xuint,CT)

Why does imhist() do this? (5)

% since Xuint is a uint-class index array, the indices are zero-based.

% that should be okay, since imhist() explicitly supports uint8 indexed images:

% "An input indexed image can be uint8, uint16, single, double, or logical."

unique(Xuint).'

ans = 1x12

0 1 2 3 4 5 6 7 8 9 10 11

<mw-icon class=""></mw-icon>

<mw-icon class=""></mw-icon>

% ... but it doesn't actually. CT(1,:) is black. There is no white in CT,

% and CT(11,:) isn't even visible.

imhist(Xuint,CT)

Why does imhist() do this? (6)

% what happens if it's a float-class index array?

% the MATLAB convention for float indexed images is to use one-based indexing

Xfloat = double(Xuint) + 1;

% well that works

imhist(Xfloat,CT)

Why does imhist() do this? (7)

% so does that mean that imhist() is breaking convention by

% treating integer-class index arrays as being 1-based?

Xmalformed = Xuint + 1;

% no, it's just an OBOE in generating the color stripe.

imhist(Xmalformed,CT)

Warning: Input has out-of-range values.

Why does imhist() do this? (8)

Also, the idea that the colorbar/histogram misalignment was a compromise for indexed color representations turns out to be wrong, since now the colorbar is aligned.

It might seem that I'm wasting my time to continue this on the forum, but someone else is likely to find the same issue(s), and it's easier to write it all out and demonstrate it here as a primary reference in the bug report. It also proves that it has nothing to do with my installation or version.

I've already started getting rid of imhist() in the MIMT passthrough tool, so I'll just make the replacement however I want. That way I can always know who to blame for bugs. :)

DGM on 20 May 2024 at 3:23

Direct link to this comment

https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3165956

  • Link

    Direct link to this comment

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3165956

Bug report submitted; replacement almost done.

FWIW, the inconsistency in the colorbar alignment between scaled and indexed images is actually a consequence of the bug with its mishandling of integer indexed images. So it's actually trying to generate the colorbar misaligned as usual (but offset by one), then it ends up overwriting the xlim of the colorbar axes anyway, because I guess either someone forgot that the xlim properties were linked, or they added that link and never checked to see what it broke.

It really was the better choice to just start over.

Sign in to comment.

Sign in to answer this question.

Answers (2)

Alex Taylor on 22 May 2024 at 17:28

  • Link

    Direct link to this answer

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#answer_1461911

  • Link

    Direct link to this answer

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#answer_1461911

Edited: Alex Taylor on 22 May 2024 at 18:39

The function imhist is one of the older functions in the Image Processing Toolbox and we on the development team are aware that imhist has a variety of questionable design choices including but not limited to:

  • Definition of histogram based on bin centers vs. bin edges.
  • Use of number of left hand side arguments to infer whether user wants plotting vs. programatic histogram binning (e.g. histcounts). This results in the behavior noted above where two calls to imhist were required to plot and compute the histogram counts and the plotting can't use the count information.

For these items, I would recommend in the near term using the modern histogram display and computation functions in MATLAB: histogram and histcounts. They can be used to address both of the behaviors above.

The main value add at this point of imhist is its visualization of image gray levels in the context of a histogram, which is not something trivial to reproduce with histogram and histcounts today. That and I suppose indexed image support for people who work with indexed images. There is also the matter of default binning behavior as a function of input datatype, so a few things.

I consider the two behaviors you noted in the misalignment of the colorbar with respect to the histogram bin center definition (and stem placement) and certainly the indexed image behavior colorbar behavior of displaying colors not present in the data in the colorbar to be a bug and will track them as such.

I appreciate the detailed discussion here and in particular your histogram code as a means of communicating your desired behavior. I can't provide any short term fixes or workarounds for the moment with these issues but we do take your bug report seriously and intend to fix both the bugs and some of the UX choices described above in a future release of the product.

1 Comment

Show -1 older commentsHide -1 older comments

DGM on 24 May 2024 at 9:57

Direct link to this comment

https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3170696

  • Link

    Direct link to this comment

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3170696

Considering the date in the file is 1992, the history of the behavior of imhist() is almost a curiosity in itself at this point. Then again, I don't need any more rabbit holes to go down at the moment. I'm glad I'm not losing my marbles anyway.

Sign in to comment.

DGM on 20 May 2024 at 5:19

  • Link

    Direct link to this answer

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#answer_1460106

  • Link

    Direct link to this answer

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#answer_1460106

Edited: DGM on 24 May 2024 at 10:03

Open in MATLAB Online

We'll see what shakes on the bug report.

I was already writing around imhist() in the MIMT tool, expecting to come back and make the fallback/passthrough paths congruent. Since all I had to do was gut the passthrough portion, I really didn't have much left to do other than improve my expectations for everything else and throw together some webdocs. I even added the axes links and event listeners like I suppose I should have in the first place (but never do).

So allow me to introduce the now-misleadingly-named imhistFB(). This used to be a passthrough to IPT imhist() with a partial fallback implementation for use when IPT was unavailable to MIMT. It doesn't use any IPT stuff anymore, but I'm not going to change the name (yet).

Given either explicit or implicit interval limits, MIMT imhistFB() can either center-align or edge-align the end bins. In both cases, the colorbar is aligned to the histogram bins, and the default gray levels correspond to the center of the relevant bin.

%% demonstrate bin alignment

clc; clf; clearvars

inpict = rand(500);

n = 5;

subplot(2,1,1)

imhistFB(inpict,n,'style','bar','binalignment','center'); % imhist()-style

subplot(2,1,2)

imhistFB(inpict,n,'style','bar','binalignment','edge');

Why does imhist() do this? (13)

As the above example shows, imhistFB() can do various plot styles. While stem() plots might be nice for large N, for small N, bars are a lot easier to read. A filled patch plot might look nice with a custom colormap.

inpict = imread('cameraman.tif');

n = 32;

subplot(3,1,1)

imhistFB(inpict,n,'style','stem'); % imhist()-style

subplot(3,1,2)

imhistFB(inpict,n,'style','bar');

subplot(3,1,3)

imhistFB(inpict,n,'style','patch');

Why does imhist() do this? (14)

You don't have to fiddle around with trying to find the axes and data range to reset the ylim if you want to see the peaks. If you did want to get the handles or the data in order to do something more complex, imhistFB() can optionally return counts, bin centers, bin edges, and both axes handles -- even when plotting, so the baloney with having to run imhist() twice or trying to externally calculate edges from centers isn't a thing anymore. The fact that that can be avoided makes complicated manipulation a lot easier to make consistent across versions. If you write code around using imhist() to draw a histogram, and then you rely on querying the properties of the underlying stem() object as I've seen recommended, your code will break in older versions.

%% demonstrate yscale styles

clc; clf; clearvars

inpict = imread('cameraman.tif');

n = 256;

subplot(2,1,1)

imhistFB(inpict,n,'yscale','tight'); % imhist()-style

subplot(2,1,2)

imhistFB(inpict,n,'yscale','full');

Why does imhist() do this? (15)

Lastly, I never intended to support indexed-color images, but it turns out that if you write things to be flexible, you can make it work just fine. I could probably add a 'indexed' flag to automatically preselect an appropriate interval for the 'range' parameter. Maybe I'll do that later.

While imhistFB() does return handles, I'm just not going to bother here, since imhist() won't anyway.

%% imhistFB() was never meant to support indexed-color images,

% but it still can do a better job than imhist() can.

clc; clf; clearvars

% this is a uint8 indexed-color image with 11 colored

% blobs on a black background (12 colors)

[Xuint CT] = imread('indexedblobs.png');

% this is a float-class copy of the same image

% offset by one, as is convention.

Xfloat = double(Xuint)+1;

% EXAMPLE 1: imhist()

% due to a bug, imhist() does not actually handle

% integer-class indexed images correctly, so it needs to be float.

% while colorbar and histogram bins were misaligned for scaled images,

% they will be aligned for no apparent reason if a colortable is provided.

% the end bins appear as half-width as expected.

subplot(4,2,1)

imhist(Xuint,CT) % uint-class (wrong colorbar)

subplot(4,2,2)

imhist(Xfloat,CT) % float-class

% EXAMPLE 2: imhistFB() with 'center' alignment similar to imhist()

% use the 'colortable' parameter to display an integer-class indexed image

% obviously, the only thing you'd need to do for a properly-offset

% float-class indexed image is to shift 'range' by 1

subplot(4,2,3)

nct = size(CT,1);

imhistFB(Xuint,nct,'colortable',CT,'range',[0 nct-1]) % uint-class

subplot(4,2,4)

imhistFB(Xfloat,nct,'colortable',CT,'range',[1 nct]) % float-class

% EXAMPLE 3: imhistFB() with 'edge' alignment

% do the same thing, but the colorbar segments all appear with uniform width.

subplot(4,2,5)

imhistFB(Xuint,nct,'colortable',CT,'range',[0 nct]-0.5,'binalignment','edge') % uint-class

subplot(4,2,6)

imhistFB(Xfloat,nct,'colortable',CT,'range',[0 nct]+0.5,'binalignment','edge') % float-class

% EXAMPLE 4: imhistFB() with 'center' alignment and setting xlim

% example 3 could also have been done with the same inputs as example 2

% and subsequent manipulation of the x-limits of both axes.

% this is what would need to be done for IPT imhist(),

% though remember that imhist()'s axes don't have linked xticks.

subplot(4,2,7)

imhistFB(Xuint,nct,'colortable',CT,'range',[0 nct-1]) % uint-class

subplot(4,2,8)

imhistFB(Xfloat,nct,'colortable',CT,'range',[1 nct]) % float-class

hax = findobj(gcf,'type','axes');

set(hax(4),'xlim',[0 nct]-0.5) % uint

set(hax(2),'xlim',[0 nct]+0.5) % float

% make sure we're seeing all the integer-value ticks for this short CT

% note that here and in example 4, i'm only updating the histogram axes

% imhistFB() axes (both the stripe and histogram) are linked on 'xlim' and 'xtick'

set(hax(4:4:12),'xtick',0:nct-1) % for any example using uint

set(hax(2:4:12),'xtick',1:nct) % for any example using float

% the imhist() axes are only linked on 'xlim', but not 'xtick',

% so we need to update both axes explicitly

set(hax(15:16),'xtick',0:nct-1) % for any example using uint

set(hax(13:14),'xtick',1:nct) % for any example using float

% set up some labels

title('uint-class','parent',hax(16))

title('float-class','parent',hax(14))

ylabel('Example 1','parent',hax(16),'fontweight','bold')

ylabel('Example 2','parent',hax(12),'fontweight','bold')

ylabel('Example 3','parent',hax(8),'fontweight','bold')

ylabel('Example 4','parent',hax(4),'fontweight','bold')

Why does imhist() do this? (16)

I've tested back to R2009b, so it should be usable for most people, even without IPT. Since I have now cursed myself by posting, I await discovering new bugs.

Using imhistFB() to represent uint12/int12-scale image data stored in a wider integer class, compared to the difficulty of other methods:

https://www.mathworks.com/matlabcentral/answers/159792-problem-with-imhist-method#answer_1460156

The motivation to add features to imhistFB() was to support the needs of the color histogram tool I was making. The ability to use custom interval limits and colormaps was important. Here's an example:

https://www.mathworks.com/matlabcentral/answers/1936059-how-can-i-plot-the-ycbcr-histograms-with-the-correct-colour-points-0-255#comment_3170661

2 Comments

Show NoneHide None

Josh on 21 May 2024 at 12:22

Direct link to this comment

https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3167586

  • Link

    Direct link to this comment

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3167586

Edited: Josh on 21 May 2024 at 12:23

respect on all the exploration, insight, and knowledge sharing...seems like you uncovered a fairly significant bug that's been present for 15+ years?

DGM on 21 May 2024 at 17:24

Direct link to this comment

https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3167916

  • Link

    Direct link to this comment

    https://www-europe.mathworks.com/matlabcentral/answers/2120156-why-does-imhist-do-this#comment_3167916

I haven't bothered to suss out the details, since debugging imhist() wouldn't solve the usability problem -- but I'm still not sure if it's more than one bug, or if it's a set of seemingly different symptoms caused by a common cause. The oldest version I have is R2009b, and imhist() is old. If there was an edit that broke intended behavior, it happened a long time ago. On the other side, I can't really even be sure whether aspects of the behavior were intended or not.

That said, it's not really terribly surprising that a bug like this could hide for a long time. I imagine 90% of imhist() usage either doesn't involve the plot generation, or it uses the default 256 bins, at which point, the misalignment isn't visually noticeable.

Sign in to comment.

Sign in to answer this question.

See Also

Categories

Image Processing and Computer VisionImage Processing ToolboxImage Filtering and EnhancementImage Arithmetic

Find more on Image Arithmetic in Help Center and File Exchange

Tags

  • imhist
  • histogram

Products

  • Image Processing Toolbox

Community Treasure Hunt

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

Start Hunting!

An Error Occurred

Unable to complete the action because of changes made to the page. Reload the page to see its updated state.


Why does imhist() do this? (19)

Select a Web Site

Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .

You can also select a web site from the following list

Americas

  • América Latina (Español)
  • Canada (English)
  • United States (English)

Europe

  • Belgium (English)
  • Denmark (English)
  • Deutschland (Deutsch)
  • España (Español)
  • Finland (English)
  • France (Français)
  • Ireland (English)
  • Italia (Italiano)
  • Luxembourg (English)
  • Netherlands (English)
  • Norway (English)
  • Österreich (Deutsch)
  • Portugal (English)
  • Sweden (English)
  • Switzerland
    • Deutsch
    • English
    • Français
  • United Kingdom(English)

Asia Pacific

Contact your local office

Why does imhist() do this? (2024)

FAQs

What is the Imhist function in Matlab? ›

The imhist function creates a histogram plot by defining n equally spaced bins, each representing a range of data values, and then calculating the number of pixels within each range. You can use the information in a histogram to choose an appropriate enhancement operation.

How to calculate histogram of an image in Matlab? ›

[ counts , binLocations ] = imhist( I ) calculates the histogram for the grayscale image I . The imhist function returns the histogram counts in counts and the bin locations in binLocations . The number of bins in the histogram is determined by the image type.

How does histogram equalization work? ›

Histogram equalization is a method to process images in order to adjust the contrast of an image by modifying the intensity distribution of the histogram. The objective of this technique is to give a linear trend to the cumulative probability function associated to the image.

How to calculate the histogram of an image? ›

The histogram is computed by examining all pixels in the image and assigning each to a bin depending on the pixel intensity. The final value of a bin is the number of pixels assigned to it.

What is the use of vectorize function in MATLAB? ›

Vectorization is one of the core concepts of MATLAB. With one command it lets you process all elements of an array, avoiding loops and making your code more readable and efficient. For data stored in numerical arrays, most MATLAB functions are inherently vectorized.

What is the function of histogram in MATLAB? ›

Description. hist( x ) creates a histogram bar chart of the elements in vector x . The elements in x are sorted into 10 equally spaced bins along the x-axis between the minimum and maximum values of x . hist displays bins as rectangles, such that the height of each rectangle indicates the number of elements in the bin.

What is the function of Laplacian in MATLAB? ›

Description. l = laplacian( f , v ) returns the Laplacian of the symbolic field f with respect to the vector v in Cartesian coordinates. If f is an array, then the function computes the Laplacian for each element of f and returns the output l that is the same size as f .

What is the NDVI function in MATLAB? ›

The ndvi function uses the 670 nm and 800 nm band reflectance values for the red and NIR band images respectively. output = ndvi( hcube ,'BlockSize', blocksize ) specifies the block size for block processing of the hyperspectral data cube by using the name-value pair argument 'BlockSize' .

Top Articles
Latest Posts
Article information

Author: Gregorio Kreiger

Last Updated:

Views: 5722

Rating: 4.7 / 5 (57 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Gregorio Kreiger

Birthday: 1994-12-18

Address: 89212 Tracey Ramp, Sunside, MT 08453-0951

Phone: +9014805370218

Job: Customer Designer

Hobby: Mountain biking, Orienteering, Hiking, Sewing, Backpacking, Mushroom hunting, Backpacking

Introduction: My name is Gregorio Kreiger, I am a tender, brainy, enthusiastic, combative, agreeable, gentle, gentle person who loves writing and wants to share my knowledge and understanding with you.