Here is practical guidance on implementing fundamental image processing techniques, such as adjusting brightness and contrast, applying geometric transformations, and smoothing images, using Python and the OpenCV library. We'll assume you have your Python environment set up with OpenCV installed, as covered in Chapter 1.First, let's ensure we have the necessary libraries imported and a sample image to work with. If you don't have a sample image readily available, you can easily find one online (search for "royalty-free images") or use a standard test image like the "Lena" or "Peppers" image, often available in computer vision datasets or easily downloadable. Save your chosen image to the same directory as your Python script or notebook.import cv2 import numpy as np import matplotlib.pyplot as plt # Load an image in color # Replace 'your_image.jpg' with the path to your image file image_path = 'your_image.jpg' image = cv2.imread(image_path) # Check if the image was loaded successfully if image is None: print(f"Error: Could not load image from {image_path}") # You might want to exit or raise an error here else: # OpenCV loads images in BGR format by default. # Matplotlib expects RGB. Let's convert for display purposes. image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Display the original image using Matplotlib plt.figure(figsize=(6, 6)) plt.imshow(image_rgb) plt.title('Original Image') plt.axis('off') # Hide axes ticks plt.show()Make sure to replace 'your_image.jpg' with the actual path to your image file. Running this code should display your original image. If you encounter an error, double-check the file path and ensure OpenCV is installed correctly.Adjusting Brightness and ContrastRemember that brightness is adjusted by adding a value to each pixel, and contrast by multiplying. Let's try this.Brightness Adjustment: We'll increase the brightness by adding a constant value (e.g., 50) to each pixel. We need to be careful not to exceed the maximum pixel value (255). OpenCV handles this automatically with functions like cv2.add.# --- Brightness Adjustment --- # Create a matrix of the same size as the image, filled with the brightness value brightness_value = 50 brightness_matrix = np.ones(image.shape, dtype=image.dtype) * brightness_value # Add the brightness matrix to the original image # cv2.add performs saturation arithmetic (clips values at 0 and 255) brighter_image = cv2.add(image, brightness_matrix) # Convert BGR to RGB for display brighter_image_rgb = cv2.cvtColor(brighter_image, cv2.COLOR_BGR2RGB) # Display the brighter image plt.figure(figsize=(6, 6)) plt.imshow(brighter_image_rgb) plt.title(f'Brightness Increased by {brightness_value}') plt.axis('off') plt.show()Contrast Adjustment: We'll increase the contrast by multiplying pixel values by a factor (e.g., 1.5). Again, cv2.multiply helps manage potential overflow.# --- Contrast Adjustment --- contrast_factor = 1.5 # Note: cv2.multiply works similarly regarding saturation # For contrast, ensure the factor is a float if needed contrast_image = cv2.multiply(image, np.array([contrast_factor])) # Clipping might be necessary manually if not using specific cv2 functions # Or ensure the multiplication doesn't cause unexpected type changes # A common way is convertScaleAbs which scales, calculates absolute values, and converts back to 8-bit contrast_image_scaled = cv2.convertScaleAbs(image, alpha=contrast_factor, beta=0) # Convert BGR to RGB for display contrast_image_rgb = cv2.cvtColor(contrast_image_scaled, cv2.COLOR_BGR2RGB) # Display the contrast-adjusted image plt.figure(figsize=(6, 6)) plt.imshow(contrast_image_rgb) plt.title(f'Contrast Increased (Factor: {contrast_factor})') plt.axis('off') plt.show()Experiment with different brightness_value (positive to increase, negative to decrease) and contrast_factor (greater than 1 for more contrast, less than 1 for less) values to see their effects.Geometric TransformationsNow let's resize, rotate, and translate our image.Scaling (Resizing): We can make the image larger or smaller using cv2.resize.# --- Scaling (Resizing) --- # Get original dimensions height, width = image.shape[:2] # Define new dimensions (e.g., half the original size) new_width = int(width * 0.5) new_height = int(height * 0.5) new_dimensions = (new_width, new_height) # Resize the image - cv2.INTER_LINEAR is a common interpolation method resized_image = cv2.resize(image, new_dimensions, interpolation=cv2.INTER_LINEAR) # Convert BGR to RGB for display resized_image_rgb = cv2.cvtColor(resized_image, cv2.COLOR_BGR2RGB) # Display the resized image plt.figure(figsize=(4, 4)) # Adjust figure size if needed plt.imshow(resized_image_rgb) plt.title(f'Resized to {new_width}x{new_height}') plt.axis('off') plt.show()Try different scaling factors and interpolation methods like cv2.INTER_CUBIC (often better quality for enlarging) or cv2.INTER_AREA (often better for shrinking).Rotation: To rotate the image, we first calculate a rotation matrix using cv2.getRotationMatrix2D and then apply it using cv2.warpAffine.# --- Rotation --- # Get image center (cX, cY) = (width // 2, height // 2) # Angle of rotation in degrees (positive for counter-clockwise) angle = 45 # Scale factor during rotation (1.0 means same size) scale = 1.0 # Calculate the rotation matrix M_rotate = cv2.getRotationMatrix2D((cX, cY), angle, scale) # Perform the rotation rotated_image = cv2.warpAffine(image, M_rotate, (width, height)) # Convert BGR to RGB for display rotated_image_rgb = cv2.cvtColor(rotated_image, cv2.COLOR_BGR2RGB) # Display the rotated image plt.figure(figsize=(6, 6)) plt.imshow(rotated_image_rgb) plt.title(f'Rotated by {angle} Degrees') plt.axis('off') plt.show()Translation (Shifting): Translation also uses cv2.warpAffine, but with a different transformation matrix. We define how much to shift along the X and Y axes.# --- Translation --- # Define shift amounts (tx: horizontal, ty: vertical) tx = 50 # Shift right by 50 pixels ty = 25 # Shift down by 25 pixels # Create the translation matrix M # [[1, 0, tx], # [0, 1, ty]] M_translate = np.float32([[1, 0, tx], [0, 1, ty]]) # Apply the translation # (width, height) defines the output image size translated_image = cv2.warpAffine(image, M_translate, (width, height)) # Convert BGR to RGB for display translated_image_rgb = cv2.cvtColor(translated_image, cv2.COLOR_BGR2RGB) # Display the translated image plt.figure(figsize=(6, 6)) plt.imshow(translated_image_rgb) plt.title(f'Translated by ({tx}, {ty})') plt.axis('off') plt.show()Notice how parts of the image might move out of the frame, and the newly exposed areas are filled with black.Basic Image Smoothing (Filtering)Finally, let's apply basic smoothing filters to reduce noise.Average Blurring: This filter replaces each pixel with the average of its neighbors within a defined kernel size.# --- Average Blurring --- # Define the kernel size (e.g., 5x5) # Larger kernel size means more blurring kernel_size_avg = (5, 5) average_blurred = cv2.blur(image, kernel_size_avg) # Convert BGR to RGB for display average_blurred_rgb = cv2.cvtColor(average_blurred, cv2.COLOR_BGR2RGB) # Display the average blurred image plt.figure(figsize=(6, 6)) plt.imshow(average_blurred_rgb) plt.title(f'Average Blurred ({kernel_size_avg[0]}x{kernel_size_avg[1]} Kernel)') plt.axis('off') plt.show()Gaussian Blurring: This uses a Gaussian kernel, giving more weight to central pixels, often resulting in a more natural-looking blur.# --- Gaussian Blurring --- # Define kernel size (must be positive and odd) kernel_size_gauss = (5, 5) # SigmaX (standard deviation in X direction). If 0, it's calculated from kernel size. sigmaX = 0 gaussian_blurred = cv2.GaussianBlur(image, kernel_size_gauss, sigmaX) # Convert BGR to RGB for display gaussian_blurred_rgb = cv2.cvtColor(gaussian_blurred, cv2.COLOR_BGR2RGB) # Display the Gaussian blurred image plt.figure(figsize=(6, 6)) plt.imshow(gaussian_blurred_rgb) plt.title(f'Gaussian Blurred ({kernel_size_gauss[0]}x{kernel_size_gauss[1]} Kernel)') plt.axis('off') plt.show()Compare the results of the Average and Gaussian filters. Gaussian blurring is generally preferred for noise reduction as it tends to preserve edges slightly better than simple averaging for the same amount of smoothing.SummaryIn this hands-on section, you've learned how to practically apply fundamental image processing techniques using OpenCV:Adjusted brightness and contrast using simple pixel arithmetic (cv2.add, cv2.multiply).Performed geometric transformations: scaling (cv2.resize), rotation (cv2.getRotationMatrix2D, cv2.warpAffine), and translation (cv2.warpAffine with a translation matrix).Applied basic smoothing filters: Average blur (cv2.blur) and Gaussian blur (cv2.GaussianBlur).These operations form the building blocks for many computer vision applications. They are often used as preprocessing steps to enhance images, normalize their appearance, or prepare them for feature extraction and analysis, which we will begin exploring in the next chapter. Keep experimenting with different parameters and images to solidify your understanding.