0

I am looking for the best way to achieve the following using Python:

  1. Import an image.
  2. Add a grid of n sections (4 shown in this example below).
  3. For each section find the dominant colour.

sky

Desired output

Output an array, list, dict or similar capturing these dominant colour values.

Maybe even a Matplotlib graph showing the colours (like pixel art).

What have I tried?

The image could be sliced using image slicer:

import image_slicer
image_slicer.slice('image_so_grid.png', 4)

I could then potentially use something like this to get the average colour but Im sure there are better ways to do this.

What are the best ways to do this with Python?

nipy
  • 5,138
  • 5
  • 31
  • 72
  • 1
    https://stackoverflow.com/questions/43111029/how-to-find-the-average-colour-of-an-image-in-python-with-opencv – Chris Apr 28 '19 at 21:53
  • Possible duplicate of [How to find the average colour of an image in Python with OpenCV?](https://stackoverflow.com/questions/43111029/how-to-find-the-average-colour-of-an-image-in-python-with-opencv) – Arne Apr 29 '19 at 07:00

2 Answers2

1

This works for 4 sections, but you'll need to figure out how to make it work for 'n' sections:

import cv2

img = cv2.imread('image.png')

def fourSectionAvgColor(image):
    rows, cols, ch = image.shape
    colsMid = int(cols/2)
    rowsMid = int(rows/2)

    numSections = 4
    section0 = image[0:rowsMid, 0:colsMid]
    section1 = image[0:rowsMid, colsMid:cols]
    section2 = image[rowsMid: rows, 0:colsMid]
    section3 = image[rowsMid:rows, colsMid:cols]
    sectionsList = [section0, section1, section2, section3]

    sectionAvgColorList = []
    for i in sectionsList:
        pixelSum = 0
        yRows, xCols, chs = i.shape
        pixelCount = yRows*xCols
        totRed = 0
        totBlue = 0
        totGreen = 0
        for x in range(xCols):
            for y in range(yRows):
                bgr = i[y,x]
                b = bgr[0]
                g = bgr[1]
                r = bgr[2]
                totBlue = totBlue+b
                totGreen = totGreen+g
                totRed = totRed+r

        avgBlue = int(totBlue/pixelCount)
        avgGreen = int(totGreen/pixelCount)
        avgRed = int(totRed/pixelCount)
        avgPixel = (avgBlue, avgGreen, avgRed)
        sectionAvgColorList.append(avgPixel)
    return sectionAvgColorList

print(fourSectionAvgColor(img))
cv2.waitKey(0)
cv2.destroyAllWindows()
Robin Alvarenga
  • 321
  • 2
  • 14
0

You can use scikit-image's view_as_blocks together with numpy.mean. You specify the block size instead of the number of blocks:

import numpy as np
from skimage import data, util
import matplotlib.pyplot as plt

astro = data.astronaut()
blocks = util.view_as_blocks(astro, (8, 8, 3))

print(astro.shape)
print(blocks.shape)

mean_color = np.mean(blocks, axis=(2, 3, 4))

fig, ax = plt.subplots()
ax.imshow(mean_color.astype(np.uint8))

Output:

(512, 512, 3)
(64, 64, 1, 8, 8, 3)

blocked and averaged

Don't forget the cast to uint8 because matplotlib and scikit-image expect floating point images to be in [0, 1], not [0, 255]. See the scikit-image documentation on data types for more info.

Juan
  • 5,433
  • 21
  • 23