Working notes from the Scandinavian Institute for Computational Vandalism

Image gradients

Image gradients are a fundamental transformation used in image processing, search indexing, and computer vision. Like a weather map showing the direction and strength of the wind, an image gradient depicts the strength and direction of changes in intensity over the surface of the image.

Through a fluke in my interpretation / understanding of the technique the gradients shown here on of images of objects from the Guttormsgaard archive, follow not the direction of the change but rather its perpendicular. As a result, the arrows chase around the edges of objects rather than piercing straight into them. Indeed, the image gradients shares a close affiliation between with the operation of edge-detection (where the movements are further segmented into discrete segments).


Following the description of Image derivatives in the book Programming Computer Vision with Python (p. 18), the following python code uses gaussian filters to approximate the derivative (rate of change) in first the x and the the y directions, and then uses a square root function to find the magnitude of change, and an arc tangent function to find the direction (or its perpendicular) at each point. The results are used to construct a second “gradient image” where the pixels color (hue in degrees on the HSL color wheel) represent the direction, and the value (or lightness) the magnitude of change.

On this page, clicking on the image switches between 3 views: (1) drawing the gradient at the mouse location, (2) drawing a grid of equally spaced gradients, and (3) showing the underlying (and pre-calated) “hsl” gradient image, which is the source of data used to perform the first two views.

from PIL import Image
from numpy import *
from scipy.ndimage import filters
import sys, json, os, math
from hsl import hsl_to_rgb
import argparse
# Based on Programming Computer Vision, chapter 1, Image Derivatives
parser = argparse.ArgumentParser(description='Calculate an image gradient.')
parser.add_argument('input', help='an image path as input')
parser.add_argument('--format', default="hslimage", help='save output format, default hslimage (direction is hue, magnitude is lightness). Other options: magimage (magnitude image), json.')
parser.add_argument('--output', help='save to output path')
args = parser.parse_args()
p = args.input
path, base = os.path.split(p)
base, ext = os.path.splitext(base)
out = args.output or os.path.join(path, base + ".gradient.png")
im = array('L'))
sigma = 5 # standard deviation
imx = zeros(im.shape)
filters.gaussian_filter(im, (sigma,sigma), (0,1), imx)
imy = zeros(im.shape)
filters.gaussian_filter(im, (sigma,sigma), (1,0), imy)
magnitude = sqrt(imx**2+imy**2)
dirs = arctan2(imy, imx)
if args.format == "hslimage":
    # Map direction to Hue, Magnitude to value
    from math import pi
    maxmag = amax(magnitude)
    height, width = magnitude.shape
    im ="RGBA", (width, height))
    for y in range(height):
        p = int(math.ceil(float(y)/height*100))
        sys.stderr.write("\rCreating gradient HSL image... [{0}%]".format(p))
        for x in range(width):
            d = dirs[y][x]
            hue = ((d+pi) / (2 * pi)) * 360
            value = (magnitude[y][x]/maxmag)
            r, g, b = hsl_to_rgb(hue, 1.0, value)
            im.putpixel((x, y), (r, g, b, 255))
elif args.format == "magimage":
    pil_im = Image.fromarray(magnitude)
elif args.format == "json":
    with open(out, "w") as f:
            'dirs': dirs.tolist(),
            'magnitudes': magnitude.tolist()
        }, f)

The code is published here.

Post a Comment

Your email is kept private. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.