A whole-image entropy calculation returns one number. A local calculation returns a map, with each output pixel describing the intensity distribution around that location. Confusing these outputs makes two correct implementations appear inconsistent. This procedure is for inspection developers who want a C++ texture map with explicit, testable behavior. You will define the neighborhood, quantize the input, implement sliding histograms, and check the result without treating entropy as a substitute for an edge detector.
1. Decide which entropy output you need
Both calculations use the same histogram-based entropy formula. Their difference is the set of pixels contributing to each histogram.
| Calculation | Histogram input | Output | Useful question |
|---|---|---|---|
| Whole-image entropy | Entire image or inspection region | One scalar | How varied is this region overall? |
| Local entropy | Neighborhood around each output location | Floating-point map | Where does local intensity diversity change? |
A single scalar cannot locate a transition. Different images can have identical whole-image histograms, and therefore identical entropy, despite completely different arrangements of their pixels.
A local map preserves some location information because the neighborhood moves. However, it still discards the spatial arrangement inside each neighborhood. Rearranging those samples without changing their histogram leaves that neighborhood's entropy unchanged.
If your actual measurement is the position of a straight transition, a stable intensity profile built by summing image rows may provide a simpler signal. Choose local entropy when the distribution of nearby intensities is itself relevant.
2. Specify the neighborhood, bins and borders
Write down these settings before comparing implementations:
- Input: one row-major, unsigned 8-bit grayscale image.
-
Neighborhood: an odd square with radius
rand side lengthk = 2r + 1. -
Quantization:
Bequal-width intensity bins, with1 <= B <= 256. - Logarithm: base two, giving entropy in bits.
- Borders: compute only complete neighborhoods; leave the surrounding margin undefined.
For example, radius 4 gives a 9 × 9 neighborhood containing 81 samples. With 32 bins, each bin covers eight consecutive input values. More bins distinguish smaller intensity differences, but can also make noise more visible.
Here, n_b is the count in bin b. Empty bins contribute zero. The upper expression is a bound; the available integer counts do not always permit reaching it exactly.
Border handling is part of the algorithm, not a display preference. Reflection introduces mirrored samples. Replication repeats edge samples. Clipping changes the neighborhood size. None is interchangeable with the others.
The implementation below uses valid neighborhoods only. Its output retains the input dimensions, but the outer r rows and columns contain NaN values. This makes uncomputed locations distinguishable from genuinely uniform regions, whose entropy is zero.
3. Implement the sliding histogram in C++
A direct implementation rebuilds a histogram for every neighborhood. A sliding implementation reuses counts as the window moves horizontally.
- Build the first histogram on each valid output row.
- Calculate entropy from its counts.
- Remove the departing column and add the entering column.
- Repeat across the row, then initialize the next row.
The following identity avoids scanning every bin after every move:
Precompute g(c) = c × log2(c), with g(0) = 0. Whenever a count changes, subtract its old contribution and add its new contribution.
This standalone implementation uses standard C++ containers. It expects contiguous grayscale pixels without row padding, and returns floating-point entropy values rather than display intensities.
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <vector>
std::vector<float> localEntropy(
const std::vector<std::uint8_t>& src,
int width, int height, int radius, int bins = 32)
{
if (width <= 0 || height <= 0 || radius < 0 ||
bins < 1 || bins > 256) {
throw std::invalid_argument("Invalid entropy settings");
}
if (radius > (std::min(width, height) - 1) / 2) {
throw std::invalid_argument("Window exceeds image");
}
const auto w = static_cast<std::size_t>(width);
const auto h = static_cast<std::size_t>(height);
if (w > std::numeric_limits<std::size_t>::max() / h ||
src.size() != w * h) {
throw std::invalid_argument("Image size mismatch");
}
const int side = 2 * radius + 1;
const auto n = static_cast<std::size_t>(side) * side;
const double logN = std::log2(static_cast<double>(n));
std::vector<double> g(n + 1, 0.0);
for (std::size_t c = 1; c <= n; ++c) {
g[c] = static_cast<double>(c) *
std::log2(static_cast<double>(c));
}
std::vector<float> out(
src.size(), std::numeric_limits<float>::quiet_NaN());
const auto binOf = [bins](std::uint8_t value) {
return static_cast<unsigned>(value) *
static_cast<unsigned>(bins) / 256u;
};
const auto index = [w](int x, int y) {
return static_cast<std::size_t>(y) * w +
static_cast<std::size_t>(x);
};
for (int y = radius; y < height - radius; ++y) {
std::array<std::size_t, 256> counts{};
double sum = 0.0;
const auto change = [&](std::uint8_t value, bool add) {
auto& count = counts[binOf(value)];
sum -= g[count];
if (add) {
++count;
} else {
--count;
}
sum += g[count];
};
for (int yy = y - radius; yy <= y + radius; ++yy) {
for (int xx = 0; xx < side; ++xx) {
change(src[index(xx, yy)], true);
}
}
for (int x = radius; x < width - radius; ++x) {
const double entropy = logN - sum / n;
out[index(x, y)] = static_cast<float>(
std::max(0.0, entropy));
if (x + 1 < width - radius) {
for (int yy = y - radius;
yy <= y + radius; ++yy) {
change(src[index(x - radius, yy)], false);
change(src[index(x + radius + 1, yy)], true);
}
}
}
}
return out;
}
The small clamp removes negative values caused by floating-point roundoff. It is not a threshold for rejecting low-entropy regions.
If your acquisition buffer has a stride, copy its active pixels into the expected layout or adapt the indexing. Respect the buffer's ownership and lifetime; the camera frame memory lifetime guide explains that integration boundary.
4. Verify the map using known patterns
Start with synthetic images, not production photographs. They provide expected answers without uncertainty about illumination or texture.
Uniform image: fill every pixel with the same value. Every valid neighborhood occupies one bin, so H = -1 × log2(1) = 0.
Two-level step: create dark and bright regions using values that enter different bins. A 9 × 9 window containing four dark columns and five bright columns has counts of 36 and 45.
Distinct samples: with 256 bins, a 3 × 3 neighborhood containing nine distinct input values has nine probabilities of 1/9. Its entropy is log2(9), approximately 3.170 bits.
Border check: with radius 4, verify that exactly the outer four rows and columns remain undefined. Do not turn those NaN values into valid zero-entropy measurements.
Also implement a slow reference version that rebuilds each histogram and evaluates the original probability formula. Compare valid pixels on small random images. An absolute tolerance such as 0.0001 bits is a reasonable initial floating-point check, not an inspection acceptance threshold. Investigate larger differences before adjusting that tolerance.
5. Separate texture evidence from edge evidence
Entropy measures intensity diversity, not gradient magnitude. A neighborhood containing two equally populated bins has one bit of entropy, regardless of how far apart those bins lie.
Quantization adds another distinction. With 32 bins, values 100 and 101 share a bin and produce no diversity by themselves. Values 100 and 108 occupy different bins. A small brightness change can therefore change entropy when samples cross bin boundaries.
A sharp two-level boundary can produce a bright band in an entropy map because its surrounding windows contain both levels. The band width depends on the neighborhood size. It does not identify the boundary with the spatial precision of a dedicated gradient operator.
Use a gradient-based method when you need edge position, orientation or contrast magnitude. Use entropy to identify changes such as smooth material becoming textured, or to generate candidate inspection regions. Then verify those candidates with a measurement suited to the defect.
For thresholded operator feedback, the live binary image preview guide covers the distinction between a displayed mask and the underlying measurement. Keep the floating-point entropy map available for diagnosis.
6. Stabilize acquisition before selecting hardware
Exposure, gain, focus and illumination can change the histogram without changing the part. Establish fixed acquisition conditions before deciding that an entropy threshold separates acceptable and defective material.
If your source contains more than eight meaningful bits, preserve it separately. The guide to saving 12-bit camera images explains the storage issue. This implementation requires an explicit conversion into its input range; use a fixed mapping rather than independently stretching every frame.
Choose among area scan cameras using acquisition requirements, not an assumed entropy capability. The camera finder helps narrow interface and shutter choices.
The LS-CMR-0053 is a GigE area scan camera with a rolling shutter. It is an acquisition candidate, not a claim of built-in entropy processing.
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Interface
- GigE
- Resolution
- 12 MP
The LS-CMR-0166 provides USB3 acquisition with a rolling shutter. Confirm that the shutter behavior suits your scene and motion.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
For deployment on vision controllers, check acquisition software support, image-buffer access and measured processing time. The MV-VC2140-128G40-1T is a vision-controller candidate to evaluate against those requirements. The catalog information does not establish entropy-processing throughput.
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
7. Measure processing cost and display consistently
For a 9 × 9 window, rebuilding every histogram requires 81 sample insertions per location. A horizontal slide instead removes nine samples and adds nine samples: 9 + 9 = 18 count changes.
This implementation rebuilds the first histogram of each output row. It then updates only the departing and entering columns. Its approximate work scales as height × side² + width × height × side, excluding border reductions. The lookup table contains N + 1 entries, while each row uses a fixed histogram array.
These operation counts are not a runtime guarantee. Measure acquisition, copying, processing and display separately on representative images. Test the required inspection region before processing an entire frame unnecessarily.
Display entropy using a fixed range across comparable images. Per-frame normalization can make a low-variation image appear as visually strong as a highly textured one.
8. Check the implementation before deployment
Use this checklist before accepting the algorithm or ordering acquisition hardware:
- Confirm grayscale conversion and input bit depth match the intended measurement.
- Record neighborhood shape, radius, bin count and logarithm base with the inspection recipe.
- Match border behavior when comparing against another implementation.
- Verify uniform, two-level and distinct-sample test cases.
- Compare the sliding implementation against a direct histogram reference.
- Exclude undefined border locations from statistics and thresholds.
- Check multiple acceptable and defective parts under normal acquisition variation.
- Confirm that noise, defocus or motion blur does not dominate the decision.
- Benchmark sustained processing rather than one isolated frame.
- Use a dedicated edge measurement when position or contrast is the actual requirement.
A square window is not appropriate for every feature. A circular footprint, masked neighborhood or spatially calibrated window may suit the task better, but each changes the sample membership and requires its own validation.
Once the measurement is stable, start the camera configuration process with your acquisition requirements. For help matching those requirements to a camera and controller, bring representative images and timing measurements to application support.
Questions
Is local image entropy the same as whole-image entropy?
Both use entropy calculated from intensity-bin probabilities, but their outputs describe different regions. Whole-image entropy produces one value for all included pixels. Local entropy repeats the calculation over moving neighborhoods, producing a map that can locate changes in intensity diversity.
How should I choose the entropy neighborhood size?
Choose a neighborhood comparable to the texture or defect scale you want to distinguish, then test nearby sizes. Smaller neighborhoods retain location detail but contain fewer samples. Larger neighborhoods combine more structure and spread responses across transitions. Keep the window fixed when comparing inspection thresholds.
Can an entropy map replace an edge detector?
Local entropy can emphasize neighborhoods that contain both sides of a boundary, but it does not directly measure gradient direction or contrast magnitude. Use it for texture evidence or candidate regions. For precise boundary location, use a dedicated edge method and validate its measurement separately.
Why does my C++ entropy map differ from another implementation?
Differences commonly come from quantization, neighborhood shape, logarithm base, border treatment or display normalization. Compare raw floating-point values on a small synthetic image first. Only expect numerical agreement after both implementations use the same samples, bins and mathematical definition.
Does higher local entropy always mean useful texture?
Not necessarily. Sensor noise and acquisition changes can spread samples across intensity bins, increasing entropy without adding useful part information. Judge the map against known defects and acceptable variation. Keep acquisition settings stable and inspect the original image alongside the entropy response.


