user574362
user574362

Reputation: 77

Computing a contrast map

I am trying to compute the contrast around each pixel in an NxN window and saving the results in a new image where each pixel in the new image is the contrast of the area around it in the old image. From another post I got this:

1) Convert the image to say LAB and get the L channel
2) Compute the max for an NxN neighborhood around each pixel
3) Compute the min for an NxN neighborhood around each pixel
4) Compute the contrast from the equation above at each pixel.
5) Insert the contrast as a pixel value in new image.

Currently I have the following:

def cmap(roi):
    max = roi.reshape((roi.shape[0] * roi.shape[1], 3)).max(axis=0)
    min = roi.reshape((roi.shape[0] * roi.shape[1], 3)).min(axis=0)
    contrast = (max - min) / (max + min)
    return contrast


def cm(img):
    # convert to LAB color space
    lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

    # separate channels
    L, A, B = cv2.split(lab)

    img_shape = L.shape

    size = 5

    shape = (L.shape[0] - size + 1, L.shape[1] - size + 1, size, size)
    strides = 2 * L.strides
    patches = np.lib.stride_tricks.as_strided(L, shape=shape, strides=strides)
    patches = patches.reshape(-1, size, size)

    output_img = np.array([cmap(roi) for roi in patches])
    cv2.imwrite("labtest.png", output_img)

The code complains about the size of roi. Is there a better (pythonic) way of doing what I want?

Upvotes: 2

Views: 1168

Answers (1)

Rotem
Rotem

Reputation: 32094

You may use Dilation and Erosion morphological operations for finding the max and min for NxN neighborhood.

  • Dilation of NxN is equivalent to maximum of NxN neighborhood.
  • Erosion of NxN is equivalent to minimum of NxN neighborhood.

Using morphological operations makes the solution much simpler than "manually" dividing the image into small blocks.

You may use the following stages:

  • Convert to LAB color space and get L channel.
  • Use "dilate" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood).
  • Use "erode" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood).
  • Convert images to type float (required before using division operation).
  • Compute contrast map (range of contrast map is [0, 1]).
  • Convert contrast map to type uint8 with rounding - the conversion loosed accuracy, so I can't recommend it (but I assume you need the conversion for getting the output as an image).

Here is a complete code sample:

import numpy as np
import cv2

size_n = 5 # NxN neighborhood around each pixel

# Read input image
img = cv2.imread('chelsea.png')

# Convert to LAB color space
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

# Get the L channel
L = lab[:, :, 0]

# Use "dilate" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood)
img_max = cv2.morphologyEx(L, cv2.MORPH_DILATE, np.ones((size_n, size_n)))

# Use "erode" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood)
img_min = cv2.morphologyEx(L, cv2.MORPH_ERODE, np.ones((size_n, size_n)))

# Convert to type float (required before using division operation)
img_max = img_max.astype(float)
img_min = img_min.astype(float)

# Compute contrast map (range of img_contrast is [0, 1])
img_contrast = (img_max - img_min) / (img_max + img_min)

# Convert contrast map to type uint8 with rounding - the conversion loosed accuracy, so I can't recommend it.
# Note: img_contrast_uint8 is scaled by 255 (scaled by 255 relative to the original formula).
img_contrast_uint8 = np.round(img_contrast*255).astype(np.uint8)

# Show img_contrast as output
cv2.imshow('img_contrast', img_contrast_uint8)
cv2.waitKey()
cv2.destroyAllWindows()

Input image:
enter image description here

L image:
enter image description here

img_max:
enter image description here

img_min:
enter image description here

Contrast map img_contrast_uint8:
enter image description here

Upvotes: 5

Related Questions