Features & Representation
Haar-like Features
Rectangle features that compare the summed intensities of adjacent image regions, computed in constant time at any position and scale from an integral image, the representation behind the Viola–Jones face detector.
beginner
Haar-like features are the simplest useful image features: each one adds up the pixel intensities inside a few adjacent rectangles and subtracts one sum from another. A two-rectangle feature placed across the eyes and the upper cheeks of a face returns a large value, because the eyes are darker than the cheeks; placed on a wall, it returns almost nothing. Paul Viola and Michael Jones made these features the basis of their 2001 face detector, together with the integral image, a table of cumulative sums from which the sum over any rectangle takes four array lookups whatever its size. That constant cost let their sliding window detector process a image at all scales at 15 frames per second on a 700 MHz Pentium III.
This article covers the features and the integral image. How Viola and Jones chose a small subset of more than a hundred thousand candidate features with AdaBoost, and how they arranged the resulting classifiers in an attentional cascade, belongs to the Viola–Jones detector.
Definition
A Haar-like feature is a weighted sum of the pixel sums of two to four rectangles placed inside a detection window, with weights of opposite sign, so that it measures the difference in average intensity between neighbouring regions. Viola and Jones used three kinds:
- a two-rectangle feature, the difference between the sums of two rectangles of the same size and shape that are adjacent horizontally or vertically (an edge detector at the scale of the rectangles);
- a three-rectangle feature, the sum of a centre rectangle minus the sums of the two rectangles on either side (a line or bar detector);
- a four-rectangle feature, the difference between the two diagonal pairs of a arrangement (a checkerboard).
Each feature is defined relative to the window by its type, position and size, and a window contains a very large number of them. The name refers to the Haar wavelet, whose two-dimensional versions have the same plus-and-minus rectangular shape.
Intuition
Faces, people and many other objects have a stable pattern of light and dark at a coarse scale, even when their fine detail varies. In frontal faces the eye region is darker than the cheeks below it and darker than the bridge of the nose between the eyes. A detector does not need the exact intensity of any pixel to use this; it needs to know whether one block of the window is darker than the block next to it.
A rectangle feature asks exactly that question. Summing over a block averages away noise and small texture, and subtracting the sums of neighbouring blocks removes the overall brightness, as a gradient does, but at the scale of a whole block instead of a pixel. Each feature is crude on its own and sees only one contrast; the representation draws its strength from the number of features in a window and from a learning algorithm that picks the few that separate the object from the background.
Formal Definition
Let be a grayscale image with intensities in , with the column and the row, and let denote the sum of over the pixels of a rectangle . A rectangle feature is
where the rectangles are adjacent and the weights have opposite signs. For Viola and Jones’s two- and four-rectangle features the rectangles have equal area and the weights are ; for the three-rectangle feature the centre is counted with and the two sides with . Lienhart and Maydt (2002) chose the weights to compensate for unequal areas, so that the feature responds with zero on a region of constant intensity: with on the whole feature and on its dark part, .
The integral image
The integral image stores, at each location, the sum of all pixels above and to the left of it, inclusive:
Viola and Jones computed it in one pass with two recurrences, a cumulative sum down each column (they call it the cumulative row sum) and a running sum of along each row:
with and . The sum over a rectangle with corners (top left) and (bottom right), inclusive, then follows from four values:
The bottom-right value includes everything above and to the left of the rectangle; subtracting the two strips to its left and above removes the region above and to the left twice, and the last term adds it back. Implementations usually pad the table with a row and column of zeros in front, so that the out-of-range terms need no special case; OpenCV’s cv::integral returns a table for a image whose entry is the sum over and .
Adjacent rectangles share corners, so a feature needs fewer than four lookups per rectangle: Viola and Jones counted six array references for a two-rectangle feature, eight for a three-rectangle feature and nine for a four-rectangle feature.
Properties
Constant cost at every scale
Building the integral image costs a few additions per pixel, once per image. After that, every rectangle sum costs the same handful of lookups whether the rectangle covers 4 pixels or 400, so the cost of a feature does not depend on its size. Direct summation over a rectangle costs additions.
This changes how a detector handles scale. A conventional detector rescales the image into an image pyramid and runs a fixed-size classifier on each level. Viola and Jones instead scaled the features, and with them the detector window, since a feature costs the same at any scale. In their journal paper (2004) they report that merely computing a 12-level pyramid of a image, with levels a factor of 1.25 apart, took about 0.05 s on the same processor, about the time their whole detector needed for the image; the scanning procedure is described in sliding window detection.
The journal paper also gives a second view of the integral image, building on the “boxlets” of Simard and colleagues. A rectangle sum is a dot product of the image with a box function that is 1 inside the rectangle and 0 outside. Integrating the image twice (along rows, then columns) and differentiating the box twice leaves the dot product unchanged, and the second derivative of a box is four delta functions at its corners, which is why four values suffice.
Normalization for lighting
Rectangle features change with contrast: halving the contrast of a window halves every feature. Viola and Jones normalized each training window to unit variance and did the same during detection, without touching the pixels: the window’s mean comes from the integral image and its sum of squares from a second integral image of , and dividing the feature value by the window’s standard deviation has the same effect as normalizing the window (by our reading, exactly so only for features whose weights cancel on a constant region, which their three-rectangle feature does not). The journal paper and the accepted version of the conference paper both print the variance as ; the identity is , with the mean of the pixel values . Lienhart and Maydt used the same two tables for the normalization , with in their experiments.
Overcompleteness and the number of features
Within a window, Viola and Jones’s base resolution for faces, the exhaustive set of rectangle features is far larger than the 576 pixels. The conference paper (CVPR 2001) gives the count as over 180,000; the journal paper (IJCV 2004) gives 160,000. A basis for the window has exactly 576 linearly independent elements, so the feature set is heavily overcomplete; the journal paper calls it about 400 times overcomplete, although its own 160,000 divided by 576 is about 280. Neither paper states how the count was made; the second example below enumerates it.
Overcompleteness is deliberate. The journal paper states that orthogonality was not central to the feature set and that the large, redundant set provides features of arbitrary aspect ratio at finely sampled positions, which empirically supported effective learning. The price is that no detector can evaluate all of them: training selects a small subset, and the face detector of the journal paper used 6,060 features in total across its stages.
Relation to Haar wavelets
The Haar wavelet is a step function: on one half of its support and on the other. Its two-dimensional versions come in three orientations (vertical, horizontal and diagonal), and at dyadic sizes and positions they form an orthogonal basis in which each coefficient encodes the difference in average intensity between adjacent regions.
Papageorgiou, Oren and Poggio (1998) used these wavelets as features for object detection. To obtain the spatial resolution needed for detection they used a “quadruple density” transform, introduced in their earlier pedestrian work (Oren et al., 1997), which shifts a wavelet of size by a quarter of its size, , in each direction and yields an overcomplete dictionary. For face images, wavelets at scales and gave 1,734 coefficients, of which they kept 37 chosen from class statistics and classified with a support vector machine; for pedestrian images they kept 29 of 1,326.
Viola and Jones described their features as reminiscent of these Haar basis functions and cited Papageorgiou et al. as a motivation. The rectangle features generalize them in three ways: rectangles can have any size, aspect ratio and position rather than dyadic ones; the set includes the three-rectangle bar, which is not a Haar wavelet; and nothing in the set is orthogonal or complete. “Haar-like” is the name that stuck, though Viola and Jones mostly called them rectangle features.
Orientations and the extended set
Viola and Jones contrasted rectangle features with steerable filters, which can be tuned to any orientation: rectangle features are sensitive to edges, bars and other simple structure, but they are coarse, and the only orientations available are vertical, horizontal and diagonal (the four-rectangle checkerboard).
Lienhart and Maydt (2002) extended the set with features rotated by 45°. Their pool has 14 prototypes, four edge features, eight line features and two centre-surround features, upright and rotated, each scaled independently in width and height. Line features with a dark centre are computed from two rectangles only, the whole feature and its centre, with the area-compensating weights above. They dropped Viola and Jones’s four-rectangle feature, arguing that it is well approximated by two of their rotated line features. They reported that a face detector trained with the extended set had, on average, about a 10% lower false alarm rate at comparable hit rates than one trained with the basic set, at comparable computational cost.
Rotated rectangles need their own table, which Lienhart and Maydt called the rotated summed area table: at each point it stores the sum over the triangular region whose apex is that point and whose sides run at 45° to the image border. It is computed in two passes over the image, and the sum over any 45° rectangle again takes four lookups, so a two-rectangle rotated feature takes eight. OpenCV’s cv::integral can return a table of the same kind, which it calls tilted, alongside the upright one; its triangles open upward from the point rather than to the left. Viewed in coordinates rotated by 45°, and , a rotated rectangle becomes an upright one and the tilted table becomes an ordinary integral image; the third example below uses this formulation, which is our own restatement rather than the paper’s.
The summed-area table
Viola and Jones noted a close relation between the integral image and the summed-area table used in computer graphics for texture mapping, citing Franklin Crow (1984), and chose a different name to emphasize its use for analysing images rather than for texture mapping.
Examples
The first example builds a synthetic face with intensities in (a bright ellipse, two dark eye blocks with a bright nose bridge between them, a dark mouth band and a little noise), evaluates three features from the integral image, checks them against direct summation, and compares the face with the same face without eyes, upside down, and with uniform noise. The last lines apply variance normalization to a dimmed copy of the face.
import numpy as np
rng = np.random.default_rng(0)
N = 24 # base window, as in Viola and Jones
yy, xx = np.mgrid[0:N, 0:N]
def synthetic_face(eyes=True):
"""24x24 face in [0, 1]: bright ellipse, dark eye band, dark mouth."""
img = np.full((N, N), 0.30) # background
img[((xx - 11.5) / 9.5) ** 2 + ((yy - 12) / 11.5) ** 2 <= 1] = 0.75
if eyes:
img[8:11, 5:10] = 0.20 # left eye
img[8:11, 14:19] = 0.20 # right eye
img[17:19, 8:16] = 0.35 # mouth
return np.clip(img + rng.normal(0, 0.02, img.shape), 0, 1)
def integral_image(img):
"""ii[y, x] = sum of img[:y, :x]; one zero row and column in front."""
ii = np.zeros((img.shape[0] + 1, img.shape[1] + 1))
ii[1:, 1:] = img.cumsum(axis=0).cumsum(axis=1)
return ii
def rect_sum(ii, x, y, w, h):
"""Sum of the w x h rectangle with top-left pixel (x, y): 4 lookups."""
return ii[y + h, x + w] - ii[y, x + w] - ii[y + h, x] + ii[y, x]
# A feature is a list of (x, y, w, h, weight); its value is the weighted sum.
features = {
# two-rectangle, vertically adjacent: cheeks (below) minus eye band (above)
"eyes vs cheeks": [(4, 8, 16, 3, -1), (4, 11, 16, 3, +1)],
# three-rectangle: nose bridge minus the two eyes (weighted to zero mean)
"bridge vs eyes": [(5, 8, 5, 3, -1), (10, 8, 4, 3, +2.5), (14, 8, 5, 3, -1)],
# two-rectangle, horizontally adjacent: right half minus left half
"left vs right": [(0, 0, 12, 24, -1), (12, 0, 12, 24, +1)],
}
def feature_value(ii, f):
return sum(wt * rect_sum(ii, x, y, w, h) for x, y, w, h, wt in f)
def feature_direct(img, f):
return sum(wt * img[y:y + h, x:x + w].sum() for x, y, w, h, wt in f)
face = synthetic_face()
patches = {
"face": face,
"face, no eyes": synthetic_face(eyes=False),
"face upside down": face[::-1],
"noise": rng.uniform(0, 1, (N, N)),
}
print(f"{'patch':<18}" + "".join(f"{k:>16}" for k in features))
worst = 0.0
for name, img in patches.items():
ii = integral_image(img)
vals = [feature_value(ii, f) for f in features.values()]
worst = max(worst, *(abs(v - feature_direct(img, f))
for v, f in zip(vals, features.values())))
print(f"{name:<18}" + "".join(f"{v:>16.2f}" for v in vals))
print(f"max |integral image - direct sum| = {worst:.1e}")
# Lighting: halve the contrast and add an offset. Dividing by the window's
# standard deviation (from integral images of I and I^2) undoes it.
def normalized(img, f):
ii, ii2 = integral_image(img), integral_image(img ** 2)
n = img.size
mean = rect_sum(ii, 0, 0, N, N) / n
std = np.sqrt(rect_sum(ii2, 0, 0, N, N) / n - mean ** 2)
return feature_value(ii, f) / std
dim = 0.5 * face + 0.2
f = features["eyes vs cheeks"]
print(f"raw: face {feature_direct(face, f):7.2f} dimmed {feature_direct(dim, f):7.2f}")
print(f"normalized: face {normalized(face, f):7.2f} dimmed {normalized(dim, f):7.2f}")
Output:
patch eyes vs cheeks bridge vs eyes left vs right
face 16.35 16.51 0.41
face, no eyes 0.22 -0.03 0.04
face upside down -5.48 0.23 0.41
noise -3.44 -1.01 -2.05
max |integral image - direct sum| = 9.2e-14
raw: face 16.35 dimmed 8.17
normalized: face 71.18 dimmed 71.18
The integral image agrees with direct summation to rounding error. The two eye features, modelled on the first two features Viola and Jones’s training selected, respond strongly only when the eyes are there: removing them drops both to about zero, because the feature regions become uniform skin. Turning the face upside down moves the dark band, and the eyes-versus-cheeks feature changes sign, since its lower rectangle now catches the eyes; the three-rectangle feature at the eyes’ former position sees no eyes at all. The left-versus-right feature is close to zero on all three faces because they are symmetric, which shows that a feature is informative only where the class has a consistent contrast. The noise patch gives values of a few units, the kind of background response that a threshold must reject. Halving the contrast halves the raw feature, and dividing by the window’s standard deviation, computed from the two integral images, removes the change exactly.
The second example counts features in a window by enumeration. The loops follow the generator of OpenCV 4.14.0’s training tool, which places every prototype at every top-left corner with every cell size that fits; its BASIC mode is Viola and Jones’s five feature types, CORE adds Lienhart and Maydt’s wider line features and the centre-surround feature, and ALL adds the 45° versions. The last lines compare the enumeration with Lienhart and Maydt’s closed-form count for one prototype.
import numpy as np
W = H = 24
# Every top-left corner (x, y) and cell size (dx, dy), as in the loops of
# CvHaarEvaluator::generateFeatures in OpenCV 4.14.0 (apps/traincascade).
x, y, dx, dy = np.meshgrid(np.arange(W), np.arange(H),
np.arange(1, W + 1), np.arange(1, H + 1), indexing="ij")
def upright(nx, ny): # prototype nx cells wide and ny cells high must fit
return int(np.sum((x + nx * dx <= W) & (y + ny * dy <= H)))
def tilted(nx, ny): # OpenCV's test: the 45-degree box (nx*dx + ny*dy wide) fits
return int(np.sum((x + nx * dx <= W) & (y + nx * dx + ny * dy <= H) & (x - ny * dy >= 0)))
viola_jones = {"two-rect, side by side": upright(2, 1), "two-rect, stacked": upright(1, 2),
"three-rect, horizontal": upright(3, 1), "three-rect, vertical": upright(1, 3),
"four-rect, diagonal": upright(2, 2)}
for k, v in viola_jones.items():
print(f"{k:<26}{v:>9,}")
basic = sum(viola_jones.values())
core = basic + upright(4, 1) + upright(1, 4) + upright(3, 3)
all_ = core + sum(tilted(*s) for s in [(2, 1), (1, 2), (3, 1), (1, 3), (4, 1), (1, 4)])
print(f"{'OpenCV BASIC (= above)':<26}{basic:>9,}\n{'OpenCV CORE':<26}{core:>9,}\n{'OpenCV ALL':<26}{all_:>9,}")
def closed_form(w, h, z=None): # Lienhart and Maydt's count; z = w + h when tilted
a, b = (z, z) if z else (w, h)
X, Y = W // a, H // b
return round(X * Y * (W + 1 - a * (X + 1) / 2) * (H + 1 - b * (Y + 1) / 2))
print(f"\n{'one prototype':<26}{'closed form':>12}{'enumerated':>12}")
for label, w, h, t in [("edge 2x1", 2, 1, False), ("line 3x1", 3, 1, False),
("tilted edge 2x1", 2, 1, True), ("tilted line 3x1", 3, 1, True)]:
n = tilted(w, h) if t else upright(w, h)
print(f"{label:<26}{closed_form(w, h, w + h if t else None):>12,}{n:>12,}")
Output:
two-rect, side by side 43,200
two-rect, stacked 43,200
three-rect, horizontal 27,600
three-rect, vertical 27,600
four-rect, diagonal 20,736
OpenCV BASIC (= above) 162,336
OpenCV CORE 210,400
OpenCV ALL 261,600
one prototype closed form enumerated
edge 2x1 43,200 43,200
line 3x1 27,600 27,600
tilted edge 2x1 8,464 12,650
tilted line 3x1 4,356 7,700
Counting both orientations of every type, Viola and Jones’s set has 162,336 features in a window, slightly more than the journal paper’s 160,000 and well short of the conference paper’s “over 180,000”. Small types dominate the count: a edge can be placed in many more sizes and positions than a checkerboard. For upright prototypes Lienhart and Maydt’s formula agrees with enumeration exactly. In the authors’ copy of their paper, Table 1 lists one count per row, each row covering a pair of mirror-image prototypes such as the horizontal and vertical edges, and its sum of 117,941 therefore counts each pair once. Three rows also differ from the formula: row 2b (the line) prints 20,736, where the formula and enumeration give 19,800; row 2f (its rotated version) prints 3,600, where the formula gives 2,500; and row 3b (the rotated centre-surround) prints 1,521 with , where gives 1,600. For rotated prototypes the formula gives fewer features than OpenCV generates; by our reading, the formula treats a rotated prototype scaled by different factors in its two directions as if its bounding box were scaled the same way, whereas the bounding box of a 45° rectangle with sides and is always . Counts thus depend on the conventions of each implementation, and published totals should be read as orders of magnitude.
The third example checks the 45° formulation on a synthetic image of a bright diagonal bar. In coordinates and , an integral image over gives the sum over any 45° rectangle in four lookups; a rotated line feature centred on the bar is compared with an upright line feature of similar size over the same bar.
import numpy as np
N = 24
yy, xx = np.mgrid[0:N, 0:N]
# A bright bar along the diagonal x = y on a dark background, in [0, 1].
img = np.where(np.abs(xx - yy) <= 1, 0.9, 0.1)
# Diagonal coordinates: s = x + y runs along the anti-diagonal, t = x - y + N - 1.
s, t = xx + yy, xx - yy + N - 1
def tilted_integral(img):
"""T[s, t] = sum of pixels with s' < s and t' < t (integral image in (s, t))."""
R = np.zeros((2 * N - 1, 2 * N - 1))
R[s, t] = img # scatter pixels onto the rotated grid
T = np.zeros((2 * N, 2 * N))
T[1:, 1:] = R.cumsum(0).cumsum(1)
return T
def diamond_sum(T, s0, s1, t0, t1): # pixels with s0 <= s < s1, t0 <= t < t1
return T[s1, t1] - T[s0, t1] - T[s1, t0] + T[s0, t0]
T = tilted_integral(img)
rng = np.random.default_rng(0)
err = 0.0
for _ in range(1000): # check against direct summation
s0, s1 = np.sort(rng.integers(0, 2 * N, 2))
t0, t1 = np.sort(rng.integers(0, 2 * N, 2))
direct = img[(s >= s0) & (s < s1) & (t >= t0) & (t < t1)].sum()
err = max(err, abs(diamond_sum(T, s0, s1, t0, t1) - direct))
print(f"max |tilted integral - direct| over 1000 rectangles = {err:.1e}")
def line(sumfn, a, b, c): # centre band minus the two flanking bands
return 2 * sumfn(*b) - sumfn(*a) - sumfn(*c)
# 45-degree line feature: three bands across t, each 3 wide, along s in [12, 36).
tilt = line(lambda *r: diamond_sum(T, *r), (12, 36, 19, 22), (12, 36, 22, 25), (12, 36, 25, 28))
# Upright line feature of similar size: three 3-pixel columns, rows 6 to 17.
up = line(lambda c0, c1: img[6:18, c0:c1].sum(), (8, 11), (11, 14), (14, 17))
print(f"45-degree line feature: {tilt:6.2f} upright line feature: {abs(up):6.2f}") # abs: no -0.00
Output:
max |tilted integral - direct| over 1000 rectangles = 5.7e-14
45-degree line feature: 57.60 upright line feature: 0.00
The rotated table reproduces every diamond-shaped sum. The rotated line feature, aligned with the bar, gives a strong response, while the upright line feature gives exactly zero: the diagonal bar crosses each of its three columns over the same number of pixels, so the centre and the flanks contain the same amount of bar. A long diagonal structure is invisible to an upright line feature wherever the structure crosses all three of its bands, which is the gap Lienhart and Maydt’s rotated features fill. The scatter onto a grid of size is a teaching device; the tables of Lienhart and Maydt and of OpenCV store the same sums on the original pixel grid.
Common Misconceptions
- “Haar-like features are Haar wavelets.” They are named after them, but they come in any size, position and aspect ratio, include three-rectangle bars, and form an overcomplete set rather than an orthogonal basis.
- “The integral image makes features cheap because the image is smaller.” The table has the same size as the image (plus one row and column) and needs more bits per entry. It makes features cheap because every rectangle sum costs four lookups regardless of the rectangle’s size. With 8-bit pixels, a table can hold values up to , which overflows 16-bit storage almost immediately; an integral image of squared intensities grows faster still.
- “A Haar-like feature detects faces.” One feature measures a single contrast and is a very weak classifier: in the conference paper, single-feature classifiers had error rates between 0.1 and 0.5. A detector combines hundreds or thousands of them.
- “Rectangle features are lighting invariant.” Features whose weights cancel on a constant region remove a constant offset, not a change of contrast; that needs the variance normalization above.
Where It Is Used
- Face detection. Haar-like features with a boosted cascade made frontal face detection run in real time on ordinary hardware in 2001. Later detectors based on convolutional neural networks learn their features instead of choosing them from a fixed rectangle set.
- Other objects and OpenCV. As of October 2026, OpenCV 4.14.0 ships trained Haar cascades for frontal and profile faces, cat faces, eyes, smiles, upper and lower bodies, full bodies and licence plates, used through its
CascadeClassifier. It keeps the source of itsopencv_traincascadetraining tool, whose-modeoption selects the BASIC, CORE or ALL feature set of the second example, but its build no longer compiles that tool (the entry is commented out inapps/CMakeLists.txt). ALL adds rotated edge and line features but no rotated centre-surround feature. In the generator, the upright three-rectangle features weight the centre so that they compute the centre minus both sides, as Viola and Jones defined them, while the four-cell line features and all rotated line features use area-compensating weights. In OpenCV 5.0.0 the cascade detector and the trained cascades moved to thexobjdetectmodule ofopencv_contrib. - Integral images in other features and filters. Box sums by integral image speed up many later methods: SURF’s box-filter approximations of Hessian and Haar wavelet responses (see feature descriptors), integral histograms for cascades of histograms of oriented gradients, local mean and variance in adaptive thresholding, and constant-time patch distances in non-local means denoising.
Related
- Sliding Window Detection
Detecting objects by running a fixed-size window classifier at every position and scale of an image, how the stride, the pyramid's scale step and the window shape set what is found and what it costs, and how the idea survives inside modern detectors.
- Object Detection
Finding every object of a set of categories in an image and returning, for each one, a bounding box, a class label and a confidence score.
- Histogram of Oriented Gradients
A dense descriptor that summarizes an image window by histograms of gradient orientations in small cells, contrast-normalized over overlapping blocks; how it is computed, why its parameters are what they are, and how its variants and implementations differ.
- Feature Descriptors
Vectors that summarize the image appearance around a keypoint so that the same scene point seen in different images gets similar vectors, from raw patches and gradient histograms to binary strings and learned embeddings.
- Image Pyramid
A multi-scale representation of an image built by repeatedly smoothing and subsampling it, so that each level holds a coarser copy at half the resolution of the one below.
- Boosting
How boosting turns many weak classifiers, such as single-feature decision stumps on image patches, into an accurate weighted vote, covering AdaBoost and its training-error bound, the exponential-loss and margin explanations, gradient boosting, sensitivity to label noise, scikit-learn's AdaBoostClassifier, and boosted feature selection in face and object detection.
References
- Viola, P. & Jones, M. (2001). Rapid Object Detection using a Boosted Cascade of Simple Features. IEEE Computer Society Conference on Computer Vision and Pattern Recognition (CVPR), 1, I-511–I-518.
- Viola, P. & Jones, M. J. (2004). Robust Real-Time Face Detection. International Journal of Computer Vision, 57(2), 137–154.
- Papageorgiou, C. P., Oren, M. & Poggio, T. (1998). A General Framework for Object Detection. IEEE International Conference on Computer Vision (ICCV), 555–562.
- Lienhart, R. & Maydt, J. (2002). An Extended Set of Haar-like Features for Rapid Object Detection. IEEE International Conference on Image Processing (ICIP), 1, I-900–I-903.
- Crow, F. C. (1984). Summed-Area Tables for Texture Mapping. ACM SIGGRAPH Computer Graphics, 18(3), 207–212.
- OpenCV (2026). Haar feature generation for opencv_traincascade, apps/traincascade/haarfeatures.cpp, OpenCV 4.14.0 source code.
- OpenCV (2026). cv::integral declaration and documentation, modules/imgproc/include/opencv2/imgproc.hpp, OpenCV 4.14.0 source code.
- OpenCV (2026). Applications build list, apps/CMakeLists.txt, OpenCV 4.14.0 source code.
- OpenCV (2026). xobjdetect module, opencv_contrib 5.0.0 source code.