Home > front end >  Computing Colorfulness of an Image in Python (Fast)
Computing Colorfulness of an Image in Python (Fast)

Time:08-25

I am asking this question because I have a suggestion about https://pyimagesearch.com/2017/06/05/computing-image-colorfulness-with-opencv-and-python/

Here, they are citing Hasler and Süsstrunk’s 2003 paper, Measuring colorfulness in natural images. They are using NumPy to perform calculations but I'm a bit concerned about the speed at which one can compute the colorfulness of an image.

CodePudding user response:

Original Solution

Here is the original code proposed by PyImageSearch Founder Adrian Rosebrock for the Python Implementation.

from imutils import build_montages
from imutils import paths
import numpy as np
import argparse
import imutils
import cv2
import time
from numba import *

image = './Oscars-selfie_620x349.png'
image=cv2.imread(image)
def image_colorfulness(image,B,G,R):
    # split the image into its respective RGB components
    # compute rg = R - G
    rg = np.absolute(R - G)
    # compute yb = 0.5 * (R   G) - B
    yb = np.absolute(0.5 * (R   G) - B)
    # compute the mean and standard deviation of both `rg` and `yb`
    (rbMean, rbStd) = (np.mean(rg), np.std(rg))
    (ybMean, ybStd) = (np.mean(yb), np.std(yb))
    # combine the mean and standard deviations
    stdRoot = np.sqrt((rbStd ** 2)   (ybStd ** 2))
    meanRoot = np.sqrt((rbMean ** 2)   (ybMean ** 2))
    # derive the "colorfulness" metric and return it
    return stdRoot   (0.3 * meanRoot)


(B, G, R) = cv2.split(image.astype("float"))
start = time.time()
for x in range(1000):
    score = image_colorfulness(image,B=B,G=G,R=R)
print(time.time() - start)

Optimized Solution (using numba)

There is a Python Package called Numba and it has been designed to go ahead and improve the speed of tasks like this by;

"Numba is designed to be used with NumPy arrays and functions. Numba generates specialized code for different array data types and layouts to optimize performance. Special decorators can create universal functions that broadcast over NumPy arrays just like NumPy functions do. "

Cite: http://numba.pydata.org/

from imutils import build_montages
from imutils import paths
import numpy as np
import argparse
import imutils
import cv2
import time
from numba import *

image = './Oscars-selfie_620x349.png'
image=cv2.imread(image)
@jit(parallel=True)
def image_colorfulness(image,B,G,R):
    # split the image into its respective RGB components
    # compute rg = R - G
    rg = np.absolute(R - G)
    # compute yb = 0.5 * (R   G) - B
    yb = np.absolute(0.5 * (R   G) - B)
    # compute the mean and standard deviation of both `rg` and `yb`
    (rbMean, rbStd) = (np.mean(rg), np.std(rg))
    (ybMean, ybStd) = (np.mean(yb), np.std(yb))
    # combine the mean and standard deviations
    stdRoot = np.sqrt((rbStd ** 2)   (ybStd ** 2))
    meanRoot = np.sqrt((rbMean ** 2)   (ybMean ** 2))
    # derive the "colorfulness" metric and return it
    return stdRoot   (0.3 * meanRoot)


(B, G, R) = cv2.split(image.astype("float"))
start = time.time()
for x in range(1000):
    score = image_colorfulness(image,B=B,G=G,R=R)
print(time.time() - start)

Benchmark

Over a Sample of Repetitive Approach of 1000 Cycles.

Original Approach: 14.722200155258179 seconds.

Numba Approach: 7.640470027923584 seconds.

CodePudding user response:

I had a try at speeding this up, just by using OpenCV functions and got a 6.5x speed-up over your Numba version on my machine:

#!/usr/bin/env python3
import numpy as np
import cv2
from numba import *

def me(image,B,G,R):
    # Use OpenCV methods wherever possible
    rg = cv2.absdiff(R,G)
    rgMean, rgStd  = cv2.meanStdDev(rg)

    avg = cv2.addWeighted(R, 0.5, G, 0.5, 0)
    yb  = cv2.absdiff(avg, B)
    ybMean, ybStd  = cv2.meanStdDev(yb)

    # combine the mean and standard deviations
    stdRoot = np.sqrt((rgStd ** 2)   (ybStd ** 2))
    meanRoot = np.sqrt((rgMean ** 2)   (ybMean ** 2))
    # derive the "colorfulness" metric and return it
    return stdRoot   (0.3 * meanRoot), rgMean, rgStd, ybMean, ybStd

@njit(parallel=True)
def image_colorfulness(image,B,G,R):
    # compute rg = R - G
    rg = np.absolute(R - G)
    # compute yb = 0.5 * (R   G) - B
    yb = np.absolute(0.5 * (R   G) - B)
    # compute the mean and standard deviation of both `rg` and `yb`
    rgMean, rgStd = np.mean(rg), np.std(rg)
    ybMean, ybStd = np.mean(yb), np.std(yb)
    # combine the mean and standard deviations
    stdRoot = np.sqrt((rgStd ** 2)   (ybStd ** 2))
    meanRoot = np.sqrt((rgMean ** 2)   (ybMean ** 2))
    # derive the "colorfulness" metric and return it
    return stdRoot   (0.3 * meanRoot), rgMean, rgStd, ybMean, ybStd


image   = cv2.imread('Oscars-selfie_620x349.png')

# OP's original method
B, G, R = cv2.split(image.astype("float"))
score, *others   = image_colorfulness(image,B=B,G=G,R=R)
print(score, others)

%timeit image_colorfulness(image,B=B,G=G,R=R)

# Using OpenCV and avoiding promotion to float
B, G, R = cv2.split(image)
score, *others   = me(image,B=B,G=G,R=R)
print(score, others)

%timeit  me(image,B=B,G=G,R=R)

Timings for original and OpenCV methods

1.11 ms ± 43.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

171 µs ± 1.49 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
  • Related