Compute an image histogram for every pixel channel
The image histogram calculator converts raw per-channel pixel values into compact frequency distributions.
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
Supply one or more named channels, choose how many equal-width bins you need, and receive an ordered count array for each channel. It is useful when image bytes have already been decoded and you need reproducible statistics without uploading or re-encoding the original file. Every value is checked against the 8-bit range, every supplied pixel contributes to exactly one bin, and an empty pixel list is rejected instead of producing a misleading all-zero result.
Prepare named channel values
Provide the decoded pixels as a channels array. Each entry has a unique channel name and a values array containing integer intensities from 0 through 255. Names such as red, green, blue, alpha, and gray are conventional, but the calculator does not impose a fixed color model; a channel may represent any component that uses the same 8-bit domain. Keep the values for a channel in their original order if that is convenient, although order does not affect a histogram because only frequency matters. Every channel must contain at least one pixel value. The request is rejected when the outer channel list is empty, when an individual values list is empty, when a name is missing or duplicated, or when a value is fractional or outside the allowed range. These checks prevent incomplete source data from looking like a valid distribution. The capability accepts up to sixteen channels and up to one million values per channel, keeping browser and API execution bounded.
Understand bin assignment and boundaries
Choose an integer bin count from 1 through 256, or omit it to use 16 bins. The full 8-bit intensity domain is divided into equal-width intervals, starting at zero and ending at 255. For 16 bins, each bin covers sixteen consecutive values: the first counts 0 through 15, the second counts 16 through 31, and the last counts 240 through 255. More generally, the bin index is the floor of the pixel value multiplied by the bin count and divided by 256. The upper endpoint is explicitly kept in the final bin, so a value of 255 is never lost to an out-of-range index. This definition is deterministic even when the selected bin count does not divide 256 evenly. Each pixel increments exactly one counter, and the counters for a channel therefore sum to its pixel_count. Use fewer bins for a compact overview and more bins when subtle intensity differences matter.
Read, compare, and validate the result
The response reports the selected bins, the fixed value_min and value_max, and one histogram object for each input channel. Each histogram preserves the channel name, states pixel_count, and returns counts in ascending intensity order. Position zero is always the darkest interval and the final position is always the brightest interval, which makes the arrays straightforward to chart or compare. Before using a result downstream, sum each counts array and confirm that it equals pixel_count; the algorithm guarantees this invariant, and checking it can expose accidental truncation elsewhere in a pipeline. Histograms summarize frequency, not spatial arrangement, so two images with very different shapes may have identical results. They also do not decode image files, infer color spaces, normalize exposure, or combine channels. Decode the source first, retain the channel interpretation needed by your application, and use this capability for the focused counting step. Browser execution keeps the supplied lists local, while API automation costs $0.002 per request.
What you can do with it
Compare color-channel distributions
Generate equally binned red, green, and blue counts before measuring how two decoded images differ.
Detect exposure extremes
Inspect the first and last intensity bins to identify channels dominated by shadows or highlights.
Validate an image-processing pipeline
Record deterministic channel histograms before and after a transformation to catch unexpected tonal changes.
FAQ
What does it cost?
API execution costs $0.002 per request. The same deterministic calculation can also run in your browser.
What pixel values are accepted?
Each value must be an integer from 0 through 255, matching an 8-bit image channel.
What happens if a pixel list is empty?
The request fails with an invalid input error. An empty list does not produce an all-zero histogram.
How are values assigned when bins do not divide 256 evenly?
Each bin index is calculated with floor(value multiplied by bins divided by 256), with 255 kept in the final bin.
Does this decode PNG, JPEG, or WebP files?
No. Supply pixel values that have already been decoded into named channel arrays.
Can channels have different numbers of pixels?
Yes. Each channel is counted independently and reports its own pixel_count.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/image/histogram \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"channels":[{"channel":"red","values":[0,15,16,127,128,240,255]},{"channel":"green","values":[0,64,64,128,192,255]}]}'const res = await fetch("https://api.kit.forhosting.com/image/histogram", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"channels": [
{
"channel": "red",
"values": [
0,
15,
16,
127,
128,
240,
255
]
},
{
"channel": "green",
"values": [
0,
64,
64,
128,
192,
255
]
}
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/image/histogram",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"channels": [
{
"channel": "red",
"values": [
0,
15,
16,
127,
128,
240,
255
]
},
{
"channel": "green",
"values": [
0,
64,
64,
128,
192,
255
]
}
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/image/histogram", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"channels":[{"channel":"red","values":[0,15,16,127,128,240,255]},{"channel":"green","values":[0,64,64,128,192,255]}]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"channels":[{"channel":"red","values":[0,15,16,127,128,240,255]},{"channel":"green","values":[0,64,64,128,192,255]}]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/image/histogram", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"channels": [
{
"channel": "red",
"values": [
0,
15,
16,
127,
128,
240,
255
]
},
{
"channel": "green",
"values": [
0,
64,
64,
128,
192,
255
]
}
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "image.histogram",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_channels | 16 |
max_values_per_channel | 1000000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |