I’m currently trying to find a way to count coloured swatches. They are all regular, same size, arranged in a chess board pattern, colours vary. The number of swatches will vary from a few hundred to about 90,000.
In this example, of only three colours, I need it to count the number of light green squares = 8
In Photoshop I can take a sample of each swatch, where x, y are the centre of the swatch, and colArr is an array of all the RGB values of those swatches.
var pointSample = app.activeDocument.colorSamplers.add([x,y]);
// Obtain array of RGB values.
var rgb = [
Math.round(pointSample.color.rgb.red,0),
Math.round(pointSample.color.rgb.green,0),
Math.round(pointSample.color.rgb.blue,0)
];
colArr.push(rgb);
delete_all_colour_samples();
function delete_all_colour_samples()
{
app.activeDocument.colorSamplers.removeAll();
}
If the colour matches one in a previously established array, it gets counted.
It works! It’s fairly instantaneous for small samples. However, with an image of over 3000 samples it takes about 25 seconds. Whilst I’m content to know that the image has been processes, I am curious if it can be done quicker.
My question is this: Could this be sped up by using say Python, but how?
2
Answers
The details of what you are asking are not quite clear to me yet – like whether the squares are always the same size in every picture, whether you know in advance how many there are in a given image, whether they are always square (you describe them as "regular") and whether you actually want to count more than one colour in each image – your example is "light green" but you seem to have an array "previously established colours" so it is not clear if you want to know how many pixels are of each colour in that array.
Anyway, let’s make a first stab at an answer, using OpenCV and basic Numpy indexing to get the centres.
That gives us this array of size [4,6,3] because Numpy arrays are [height, width, channels] and we have 4 swatch centres vertically, 6 swatch centres horizontally and each has 3 colours.
In case you are unfamiliar with Numpy indexing, it is basically:
so in the code above I am starting half a swatch width into the array (to get to the centre of the first swatch) and then using a stride equal to the swatch width to get to the next centre.
If you now want to tally the number of swatch centres having color [141,244,159], you can do:
which gets the result
8
.Note that you can do this all with Python Imaging Library PIL/Pillow, if that is an easier install for you, by replacing:
with:
and bear in mind that OpenCV uses BGR ordering whereas PIL uses RGB, so the colour triplets you are looking for will be reversed.
I generated a sample image of 90,000 swatches of 10×10 pixels using 1,000 unique colours like this:
I then timed it and got 1.9ms and it found 83 swatches with colour [0, 78, 156] which is what you’d expect with 90,000 swatches of 1,000 colours:
To count the number of contiguous regions with a given color:
Most image processing libraries will have the tools needed to do this easily. For example using DIPlib (disclosure: I’m an author) it would be:
For OpenCV it would be:
There are two things that can go wrong here:
The colors are not exact (illumination changes, compression artifacts, etc.). In this case you need to use a range of colors (
dip.InRange(img, color1, color2)
instead ofimg == color
, orcv2.inRange(img, color1, color2)
).The squares are not perfectly separated like in the example. If the joins happen only at vertices, a small erosion of the binary
squares
image before labeling should do the trick. If the squares can be side-by-side, a more complex method is needed: we’d look at the histogram of sizes, expecting to see a first peak at the size of individual squares, and further peaks at multiples of this size. From this histogram we can compute how many squares there are, by multiplying bins near the 2x size by 2, near the 3x size by 3, etc, and summing up all our values.