Detect "Overall Average" Color of the Picture

Detect overall average color of the picture

You can use PHP to get an array of the color palette like so:

<?php 
function colorPalette($imageFile, $numColors, $granularity = 5)
{
$granularity = max(1, abs((int)$granularity));
$colors = array();
$size = @getimagesize($imageFile);
if($size === false)
{
user_error("Unable to get image size data");
return false;
}
$img = @imagecreatefromjpeg($imageFile);
// Andres mentioned in the comments the above line only loads jpegs,
// and suggests that to load any file type you can use this:
// $img = @imagecreatefromstring(file_get_contents($imageFile));

if(!$img)
{
user_error("Unable to open image file");
return false;
}
for($x = 0; $x < $size[0]; $x += $granularity)
{
for($y = 0; $y < $size[1]; $y += $granularity)
{
$thisColor = imagecolorat($img, $x, $y);
$rgb = imagecolorsforindex($img, $thisColor);
$red = round(round(($rgb['red'] / 0x33)) * 0x33);
$green = round(round(($rgb['green'] / 0x33)) * 0x33);
$blue = round(round(($rgb['blue'] / 0x33)) * 0x33);
$thisRGB = sprintf('%02X%02X%02X', $red, $green, $blue);
if(array_key_exists($thisRGB, $colors))
{
$colors[$thisRGB]++;
}
else
{
$colors[$thisRGB] = 1;
}
}
}
arsort($colors);
return array_slice(array_keys($colors), 0, $numColors);
}
// sample usage:
$palette = colorPalette('rmnp8.jpg', 10, 4);
echo "<table>\n";
foreach($palette as $color)
{
echo "<tr><td style='background-color:#$color;width:2em;'> </td><td>#$color</td></tr>\n";
}
echo "</table>\n";

Which gives you an array whose values are higher for how often that color has been used.

EDIT
A commenter asked how to use this on all files in a directory, here it is:

    if ($handle = opendir('./path/to/images')) {

while (false !== ($file = readdir($handle))) {
$palette = colorPalette($file, 10, 4);
echo "<table>\n";
foreach($palette as $color) {
echo "<tr><td style='background-color:#$color;width:2em;'> </td><td>#$color</td></tr>\n";
}
echo "</table>\n";
}
closedir($handle);
}

might not want to do this on too many files, but it's your server.

Alternatively if you'd rather use Javascript Lokesh's Color-Theif library does exactly what you're looking for.

Get average color of image without sacrificing lazy loading

You can create a load event listener for each image, so once each image loads you get the color. Your code can be written thus:

var imgg = document.getElementsByClassName("cimg");
var blocks = document.getElementsByClassName("block");
for (var i = 0; i < imgg.length; i++) {
setColor(i);
}

function setColor(i) {
var $img = imgg[i];
// once the lazy-loaded image loads:
$img.addEventListener("load", e => {
// get average color and set
var rgb = averageColor($img);
blocks[i].style.backgroundColor =
'rgb(' + rgb.r + ','
+ rgb.g + ','
+ rgb.b + ')';
});
}

Get average color of image via Javascript

AFAIK, the only way to do this is with <canvas/>...

DEMO V2: http://jsfiddle.net/xLF38/818/

Note, this will only work with images on the same domain and in browsers that support HTML5 canvas:

function getAverageRGB(imgEl) {

var blockSize = 5, // only visit every 5 pixels
defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
canvas = document.createElement('canvas'),
context = canvas.getContext && canvas.getContext('2d'),
data, width, height,
i = -4,
length,
rgb = {r:0,g:0,b:0},
count = 0;

if (!context) {
return defaultRGB;
}

height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

context.drawImage(imgEl, 0, 0);

try {
data = context.getImageData(0, 0, width, height);
} catch(e) {
/* security error, img on diff domain */
return defaultRGB;
}

length = data.data.length;

while ( (i += blockSize * 4) < length ) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i+1];
rgb.b += data.data[i+2];
}

// ~~ used to floor values
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);

return rgb;

}

For IE, check out excanvas.

How to find average color of an image with ImageMagick?

You can do the following to parse out just the comma-separated RGB values. It also will not return text color names.

convert cat.png -resize 1x1\! \
-format "%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]" info:-

Output format should look like:

155,51,127

This should work in ImageMagick 6.3.9.1+

How can I get the average color of an image?

Bitmap bitmap = someFunctionReturningABitmap();
long redBucket = 0;
long greenBucket = 0;
long blueBucket = 0;
long pixelCount = 0;

for (int y = 0; y < bitmap.getHeight(); y++)
{
for (int x = 0; x < bitmap.getWidth(); x++)
{
Color c = bitmap.getPixel(x, y);

pixelCount++;
redBucket += Color.red(c);
greenBucket += Color.green(c);
blueBucket += Color.blue(c);
// does alpha matter?
}
}

Color averageColor = Color.rgb(redBucket / pixelCount,
greenBucket / pixelCount,
blueBucket / pixelCount);

How to calculate the average color of a UIImage?

You'll need to use the Accelerate Library, Apple has a manual with some sample code, it'll work in Swift or ObjC

Here is a sample to get you going, I use this to calculate a person's heart rate and heart rate variability using the change in colors of a finger over the camera lens.

Full code here:
https://github.com/timestocome/SwiftHeartRate/blob/master/Swift%20Pulse%20Reader/ViewController.swift

It's in an older version of Swift but I think you'll get the idea. I was doing this at 240 fps, but with a cropped smaller section of the image.

Relevant code here:

// compute the brightness for reg, green, blue and total
// pull out color values from pixels --- image is BGRA
var greenVector:[Float] = Array(count: numberOfPixels, repeatedValue: 0.0)
var blueVector:[Float] = Array(count: numberOfPixels, repeatedValue: 0.0)
var redVector:[Float] = Array(count: numberOfPixels, repeatedValue: 0.0)

vDSP_vfltu8(dataBuffer, 4, &blueVector, 1, vDSP_Length(numberOfPixels))
vDSP_vfltu8(dataBuffer+1, 4, &greenVector, 1, vDSP_Length(numberOfPixels))
vDSP_vfltu8(dataBuffer+2, 4, &redVector, 1, vDSP_Length(numberOfPixels))

// compute average per color
var redAverage:Float = 0.0
var blueAverage:Float = 0.0
var greenAverage:Float = 0.0

vDSP_meamgv(&redVector, 1, &redAverage, vDSP_Length(numberOfPixels))
vDSP_meamgv(&greenVector, 1, &greenAverage, vDSP_Length(numberOfPixels))
vDSP_meamgv(&blueVector, 1, &blueAverage, vDSP_Length(numberOfPixels))

// convert to HSV ( hue, saturation, value )
// this gives faster, more accurate answer
var hue: CGFloat = 0.0
var saturation: CGFloat = 0.0
var brightness: CGFloat = 0.0
var alpha: CGFloat = 1.0

var color: UIColor = UIColor(red: CGFloat(redAverage/255.0), green: CGFloat(greenAverage/255.0), blue: CGFloat(blueAverage/255.0), alpha: alpha)
color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)

// 5 count rolling average
let currentHueAverage = hue/movingAverageCount
movingAverageArray.removeAtIndex(0)
movingAverageArray.append(currentHueAverage)

let movingAverage = movingAverageArray[0] + movingAverageArray[1] + movingAverageArray[2] + movingAverageArray[3] + movingAverageArray[4]

Python - average color of part of an image

As your region of interest (ROI) is only a simple rectangle, I think you just want to use Numpy slicing to identify it.

So, I have made a test image that is green where you want to measure:

Sample Image

Then the code would go like this:

import cv2
import numpy as np

# Load the image
im = cv2.imread('start.png')

# Calculate mean of green area
A = np.mean(im[600:640, 20:620], axis=(0,1))

That gets green, unsurprisingly:

array([  0., 255.,   0.])

Now include some of the black area above the green to reduce the mean "greenness"

B = np.mean(im[500:640, 20:620], axis=(0,1))

That gives... "a bit less green":

aarray([ 0.        , 72.85714286,  0.        ])

The full sampling of every pixel in the green area takes 214 microsecs on my Mac, as follows:

IIn [5]: %timeit A = np.mean(im[600:640, 20:620], axis=(0,1))
214 µs ± 150 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Note that you could almost certainly sample every 4th pixel down and every 4th pixel across as follows in 50.6 microseconds and still get a very indicative result:

In [11]: %timeit A = np.mean(im[500:640:4, 20:620:4], axis=(0,1))
50.6 µs ± 29.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

You can make every pixel you are sampling into a red dot like this - look carefully:

im[600:640:4, 20:620:4] = [255,0,0]

Sample Image


As suggested by Fred (@fmw42), it is even faster if you replace np.mean() with cv2.mean():

So, 11.4 microseconds with cv2.mean() versus 214 microseconds with np.mean():

In [22]: %timeit cv2.mean(im[600:640, 20:620])
11.4 µs ± 11.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

And 7.85 microseconds with cv2.mean() versus 50.6 microseconds with np.mean() if sampling every 4th pixel:

In [23]: %timeit cv2.mean(im[600:640:4, 20:620:4])
7.85 µs ± 6.42 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


Related Topics



Leave a reply



Submit