C · Course navigation

C: grayscale histogram and a global threshold

Count how many pixels have each brightness level and separate dark from bright pixels at the mean.

On this page
Before you start

C arrays and integer arithmetic.

  • Count how many pixels have each brightness level and separate dark from bright pixels at the mean.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

A histogram counts, for each brightness level, how many pixels have that value. It summarizes an image's tonal range without storing its layout. A cluster of dark values and a cluster of bright values show up as two peaks, which suggests a natural place to separate foreground from background.

The simplest global threshold is the mean brightness. Pixels above it become foreground (1) and the rest background (0). Methods such as Otsu's choose the threshold that best separates the two groups. This program uses 8 brightness bins of width 32 to keep the output short and then thresholds a small 4×4 image at its mean.

Code example

C
#include <stdio.h>

int main(void) {
  unsigned char image[4][4] = {
    {12, 20, 200, 210},
    {15, 25, 220, 205},
    {10, 30, 215, 230},
    {18, 22, 225, 240},
  };
  int bins[8] = {0};
  int total = 0;

  for (int r = 0; r < 4; r++) {
    for (int c = 0; c < 4; c++) {
      bins[image[r][c] / 32]++;
      total += image[r][c];
    }
  }
  int mean = total / 16;

  printf("histogram:");
  for (int b = 0; b < 8; b++) {
    printf(" %d", bins[b]);
  }
  printf("\nmean brightness: %d\n", mean);

  for (int r = 0; r < 4; r++) {
    for (int c = 0; c < 4; c++) {
      putchar(image[r][c] > mean ? '#' : '.');
    }
    putchar('\n');
  }
  return 0;
}
Output
histogram: 8 0 0 0 0 0 5 3
mean brightness: 118
..##
..##
..##
..##

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1#include <stdio.h>
2
3int main(void) {
4 unsigned char image[4][4] = {
5 {12, 20, 200, 210},
6 {15, 25, 220, 205},
7 {10, 30, 215, 230},
8 {18, 22, 225, 240},
9 };
10 int bins[8] = {0};
11 int total = 0;
12
13 for (int r = 0; r < 4; r++) {
14 for (int c = 0; c < 4; c++) {
15 bins[image[r][c] / 32]++;
16 total += image[r][c];
17 }
18 }
19 int mean = total / 16;
20
21 printf("histogram:");
22 for (int b = 0; b < 8; b++) {
23 printf(" %d", bins[b]);
24 }
25 printf("\nmean brightness: %d\n", mean);
26
27 for (int r = 0; r < 4; r++) {
28 for (int c = 0; c < 4; c++) {
29 putchar(image[r][c] > mean ? '#' : '.');
30 }
31 putchar('\n');
32 }
33 return 0;
34}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Each pixel is placed in a bin by its brightness: 0–31 in bin 0, 32–63 in bin 1, and so on.—
—
The mean of the 16 pixels is the threshold: (152 + 1745) / 16 = 118.mean = 118
—
Eight pixels are dark (bin 0) and eight are bright (bins 6 and 7). The mean lies between the two clusters.mean = 118
histogram: 8 0 0 0 0 0 5 3
mean brightness: 118
Pixels brighter than 118 become #, the rest a dot.mean = 118
histogram: 8 0 0 0 0 0 5 3
mean brightness: 118
..##
..##
..##
..##

Behind the scenes

How C processes this code

The C compiler produces a native program. Variables occupy memory, and execution enters main.

Cost and common mistakes

O(p) time for p pixels and O(bins) extra space.

  • A pixel value of 255 divided by 32 is 7, so 8 bins of width 32 cover 0 to 255 with no out-of-range index.
  • A mean threshold works when the dark and bright groups are about the same size. Otherwise a method like Otsu's is better.
  • unsigned char holds 0 to 255. Adding pixels needs a wider type such as int.

Try it yourself

How many bins would an exact 256-level histogram need?

Reveal answer and reasoning

256 bins, one for each possible 8-bit brightness value.

Further reading