Histogram Plotting and stretching in Python

Histogram plotting and stretching is a powerful technique in image processing and data visualization that allows you to represent the distribution of pixel intensities and improve image contrast. This process enhances visibility by spreading pixel values across the full intensity range (0-255).

A histogram shows the frequency distribution of pixel intensities in an image. Histogram stretching (also called contrast stretching) improves image contrast by mapping the current range of pixel values to utilize the full available range.

Method 1: Using Built-in Functions

OpenCV and Matplotlib provide built-in functions for histogram equalization and plotting. Here's how to plot and stretch histograms using these libraries ?

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Load sample image (requires external image file)
img = cv2.imread('sample.jpg', cv2.IMREAD_COLOR)

# Split into RGB channels
blue, green, red = cv2.split(img)

# Plot histograms for each channel
plt.figure(figsize=(10, 4))
plt.hist(red.ravel(), bins=256, color='red', alpha=0.7, label='Red')
plt.hist(green.ravel(), bins=256, color='green', alpha=0.7, label='Green') 
plt.hist(blue.ravel(), bins=256, color='blue', alpha=0.7, label='Blue')
plt.xlabel('Pixel Intensity')
plt.ylabel('Frequency')
plt.title('Original Image Histograms')
plt.legend()
plt.show()

# Apply histogram equalization to each channel
red_stretched = cv2.equalizeHist(red)
green_stretched = cv2.equalizeHist(green)
blue_stretched = cv2.equalizeHist(blue)

# Merge channels back together
img_stretched = cv2.merge((blue_stretched, green_stretched, red_stretched))

# Display original and stretched images side by side
plt.figure(figsize=(12, 5))
plt.subplot(121)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.axis('off')

plt.subplot(122)
plt.imshow(cv2.cvtColor(img_stretched, cv2.COLOR_BGR2RGB))
plt.title('Histogram Equalized Image')
plt.axis('off')
plt.show()

Method 2: Manual Implementation

For educational purposes, here's how to implement histogram calculation and linear stretching without built-in functions ?

import numpy as np
import matplotlib.pyplot as plt

# Create sample image data for demonstration
np.random.seed(42)
img = np.random.randint(50, 200, (100, 100, 3), dtype=np.uint8)

# Split into RGB channels manually
red = img[:,:,0]
green = img[:,:,1] 
blue = img[:,:,2]

# Calculate histograms manually
def calculate_histogram(channel):
    histogram = np.zeros(256, dtype=int)
    height, width = channel.shape
    
    for i in range(height):
        for j in range(width):
            pixel_value = channel[i, j]
            histogram[pixel_value] += 1
    
    return histogram

# Calculate histograms for each channel
hist_red = calculate_histogram(red)
hist_green = calculate_histogram(green)
hist_blue = calculate_histogram(blue)

# Plot histograms
plt.figure(figsize=(10, 4))
plt.plot(range(256), hist_red, color='red', alpha=0.7, label='Red')
plt.plot(range(256), hist_green, color='green', alpha=0.7, label='Green')
plt.plot(range(256), hist_blue, color='blue', alpha=0.7, label='Blue')
plt.xlabel('Pixel Intensity')
plt.ylabel('Frequency')
plt.title('Manual Histogram Calculation')
plt.legend()
plt.show()

print("Original intensity ranges:")
print(f"Red: {red.min()} - {red.max()}")
print(f"Green: {green.min()} - {green.max()}")
print(f"Blue: {blue.min()} - {blue.max()}")
Original intensity ranges:
Red: 50 - 199
Green: 50 - 199
Blue: 50 - 199

Linear Contrast Stretching

# Manual linear stretching function
def linear_stretch(channel):
    min_val = np.min(channel)
    max_val = np.max(channel)
    
    # Avoid division by zero
    if max_val == min_val:
        return channel
    
    # Linear transformation: new_value = (old_value - min) * 255 / (max - min)
    stretched = ((channel.astype(float) - min_val) * 255 / (max_val - min_val))
    return stretched.astype(np.uint8)

# Apply stretching to each channel
red_stretched = linear_stretch(red)
green_stretched = linear_stretch(green)
blue_stretched = linear_stretch(blue)

# Create stretched image
img_stretched = np.stack([red_stretched, green_stretched, blue_stretched], axis=2)

print("Stretched intensity ranges:")
print(f"Red: {red_stretched.min()} - {red_stretched.max()}")
print(f"Green: {green_stretched.min()} - {green_stretched.max()}")
print(f"Blue: {blue_stretched.min()} - {blue_stretched.max()}")

# Display comparison
plt.figure(figsize=(12, 5))
plt.subplot(121)
plt.imshow(img)
plt.title('Original Image')
plt.axis('off')

plt.subplot(122)
plt.imshow(img_stretched)
plt.title('Linear Stretched Image')
plt.axis('off')
plt.show()
Stretched intensity ranges:
Red: 0 - 255
Green: 0 - 255
Blue: 0 - 255

Key Differences

Technique Method Result Use Case
Histogram Equalization cv2.equalizeHist() Uniform distribution General contrast enhancement
Linear Stretching Manual scaling Maintains relative relationships Preserving image characteristics

Practical Applications

Histogram stretching is commonly used in:

  • Medical imaging Enhancing X-ray and MRI contrast
  • Satellite imagery Improving visibility of terrain features
  • Photography Correcting underexposed or low-contrast images
  • Computer vision Preprocessing for better feature detection

Conclusion

Histogram plotting reveals pixel intensity distributions, while stretching improves image contrast by utilizing the full intensity range. Use built-in functions like cv2.equalizeHist() for efficiency, or implement manual stretching when you need precise control over the transformation process.

---
Updated on: 2026-03-27T07:49:16+05:30

1K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements