Hosted with nbsanity. See source notebook on GitHub.

AI Prompt

I am building a bacteria detecting model using pretrained yolo model with oriented bounding box (OBB). Here, I am facing a problem with the images where images contain bacteria with varying brightness. Some bacteria are much brighter than some others, and some end up with brightness close to the background. When labeling, we used sliders to only show pixels within certain boundaries to display low signaling bacteria. Is there any image processing technique that could somehow brighten all the bacteria?

AI Response

This is a common challenge in microscopy image processing, and there are several techniques we can use to enhance the visibility of bacteria with varying brightness levels. Here are some approaches we could try:

  1. Adaptive Histogram Equalization (specifically CLAHE - Contrast Limited Adaptive Histogram Equalization):
  1. Local contrast enhancement:
  1. Multi-scale enhancement:

Would you like me to show you how to implement any of these approaches? We could start with CLAHE as it’s often very effective for microscopy images. Would that be helpful?

The nice thing about these methods is that they can be applied as a preprocessing step before feeding the images to your YOLO model.

AI Prompt

okay, let’s start with CLAHE

AI Response

I’ll help you implement CLAHE using OpenCV. First, let’s write a basic implementation and then we can adjust parameters as needed.

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

def apply_clahe(image, clip_limit=2.0, tile_grid_size=(8,8)):
    # Convert to uint8 if not already
    if image.dtype != np.uint8:
        image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    
    # Create CLAHE object
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
    
    # Apply CLAHE
    enhanced = clahe.apply(image)
    
    return enhanced

# Function to display original and enhanced images side by side
def display_comparison(original, enhanced):
    plt.figure(figsize=(12,6))
    plt.subplot(121)
    plt.imshow(original, cmap='gray')
    plt.title('Original Image')
    plt.axis('off')
    
    plt.subplot(122)
    plt.imshow(enhanced, cmap='gray')
    plt.title('CLAHE Enhanced')
    plt.axis('off')
    
    plt.show()

Could you share a sample image so we can test this and adjust the parameters? The key parameters we can tune are:

  1. clip_limit: Controls contrast enhancement (higher values = more enhancement but might increase noise)
  2. tile_grid_size: Size of grid for local histogram equalization (smaller grid = more local enhancement)

If you don’t want to share an image, you can try this code with your images and let me know how it works. We can then adjust the parameters based on your results.

Would you like to try this first, or would you prefer me to explain more about how CLAHE works?

AI Prompt

how does CLAHE work?

AI Response

Let me explain how CLAHE (Contrast Limited Adaptive Histogram Equalization) works:

  1. Basic Principle:
  1. Step-by-Step Process:
    • The image is divided into small tiles (defined by tile_grid_size)
    • For each tile:
      • A local histogram is computed
      • The histogram is clipped at a predetermined value (clip_limit) to prevent noise amplification
      • Histogram equalization is applied to that tile
    • Bilinear interpolation is used to eliminate artificial boundaries between tiles
  2. Key Advantages:
    • Better handling of local contrast
    • Prevents over-amplification of noise (through clipping)
    • Preserves edges and local details
    • Works well with varying brightness levels in different image regions
  3. Parameters Impact:
    • clip_limit: Higher values allow more contrast enhancement but may increase noise
    • tile_grid_size: Smaller tiles give more local enhancement but might make the image look “patchy”

Would you like to try the implementation now with your images? We can start with the default parameters (clip_limit=2.0, tile_grid_size=(8,8)) and adjust them based on your specific needs.

AI Prompt

I think increasing clip_limit to 10 is good

AI Response

Let’s modify the code to use a higher clip_limit of 10. This will allow for stronger contrast enhancement, which might help make dimmer bacteria more visible. Here’s the updated code:

def apply_clahe(image, clip_limit=10.0, tile_grid_size=(8,8)):
    # Convert to uint8 if not already
    if image.dtype != np.uint8:
        image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    
    # Create CLAHE object with higher clip limit
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
    
    # Apply CLAHE
    enhanced = clahe.apply(image)
    
    return enhanced

Try this with your images and let me know if: 1. The bacteria visibility has improved 2. The noise level is acceptable 3. You’d like to adjust the tile_grid_size as well

If you need any further adjustments or want to try another approach, just let me know!

AI Prompt

I think the bright ones are too bright. Is there any way to reduce the brightness of the brithest ones?

AI Response

Yes, we can handle this by adding intensity capping or compression for the brightest pixels. Here’s a modified version that includes gamma correction and optional intensity capping:

def enhance_bacteria(image, clip_limit=10.0, tile_grid_size=(8,8), gamma=1.2, max_percentile=99):
    # Convert to uint8 if not already
    if image.dtype != np.uint8:
        image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    
    # Cap very bright intensities using percentile
    threshold = np.percentile(image, max_percentile)
    image = np.minimum(image, threshold)
    
    # Renormalize to full range
    image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    
    # Apply gamma correction to compress high intensities
    image = np.power(image / 255.0, gamma) * 255.0
    image = image.astype(np.uint8)
    
    # Apply CLAHE
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
    enhanced = clahe.apply(image)
    
    return enhanced

This version: 1. Caps very bright pixels at the 99th percentile (adjustable via max_percentile) 2. Applies gamma correction (gamma > 1 compresses bright regions) 3. Then applies CLAHE

You can adjust: - max_percentile: Lower values will cap more bright pixels (try 95-99) - gamma: Higher values compress bright regions more (try 1.0-1.5)

Try this and let me know if it better balances the bright and dim bacteria. We can further tune the parameters based on your results.

AI Prompt

Why is it converting the image to unit8?

AI Response

The conversion to uint8 (8-bit unsigned integer) is done because:

  1. OpenCV’s CLAHE implementation expects 8-bit images (values from 0-255)
  2. uint8 is the standard format for most image processing operations

However, if you’re working with microscopy data, you might have: - Higher bit depth images (12-bit, 16-bit) - Float values - Greater dynamic range

If this is the case, we can modify the code to preserve more of the original dynamic range. Here’s a version that handles higher bit depths:

def enhance_bacteria_highbit(image, clip_limit=10.0, tile_grid_size=(8,8), gamma=1.2, max_percentile=99):
    # Store original dtype and range
    original_dtype = image.dtype
    original_max = np.max(image)
    
    # Work with float values for better precision
    image_float = image.astype(np.float32)
    
    # Cap very bright intensities using percentile
    threshold = np.percentile(image_float, max_percentile)
    image_float = np.minimum(image_float, threshold)
    
    # Normalize to [0,1] range for gamma correction
    image_float = (image_float - np.min(image_float)) / (np.max(image_float) - np.min(image_float))
    
    # Apply gamma correction
    image_float = np.power(image_float, gamma)
    
    # Convert to uint8 only for CLAHE operation
    image_uint8 = (image_float * 255).astype(np.uint8)
    
    # Apply CLAHE
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
    enhanced_uint8 = clahe.apply(image_uint8)
    
    # Convert back to original range and dtype
    enhanced_float = enhanced_uint8.astype(np.float32) / 255.0
    enhanced = (enhanced_float * original_max).astype(original_dtype)
    
    return enhanced

This version: 1. Preserves the original data type and range 2. Performs calculations in floating point for better precision 3. Only converts to uint8 temporarily for CLAHE 4. Returns the result in the original format

Would this be more suitable for your microscopy data?

AI Prompt

I have 16 bit data

AI Response

Ah yes, 16-bit microscopy data is common and has a range of 0-65535 (2^16 - 1), giving much better dynamic range than 8-bit. Here’s a version specifically optimized for 16-bit data:

def enhance_bacteria_16bit(image, clip_limit=10.0, tile_grid_size=(8,8), gamma=1.2, max_percentile=99):
    assert image.dtype == np.uint16, "Input image should be 16-bit (uint16)"
    
    # Work with float values for better precision
    image_float = image.astype(np.float32)
    
    # Cap very bright intensities using percentile
    threshold = np.percentile(image_float, max_percentile)
    image_float = np.minimum(image_float, threshold)
    
    # Normalize to [0,1] range for gamma correction
    image_float = (image_float - np.min(image_float)) / (np.max(image_float) - np.min(image_float))
    
    # Apply gamma correction
    image_float = np.power(image_float, gamma)
    
    # Convert to uint8 only for CLAHE operation
    image_uint8 = (image_float * 255).astype(np.uint8)
    
    # Apply CLAHE
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
    enhanced_uint8 = clahe.apply(image_uint8)
    
    # Convert back to 16-bit range
    enhanced = (enhanced_uint8.astype(np.float32) / 255.0 * 65535).astype(np.uint16)
    
    return enhanced

Key changes in this version: 1. Added assertion to ensure 16-bit input 2. Uses full 16-bit range (0-65535) for the output 3. Maintains precision during processing steps

Try this with your 16-bit images. Let me know if you’d like to adjust any parameters or if you notice any loss of detail in important intensity ranges.

Using pytorch pt format

Loading model and finetuning models

AI Prompt

This is great. I should be able to train better my model with those enhancements. I was going to use ultralytics, but I think it is better to just build my own library to train. Is it something I can do?

I want to use yolo’s official model, such as yolo11n-obb.pt and finetune this for my own use.

AI Response

Yes, you can definitely build your own training pipeline for OBB-YOLO! Since you want to use the official YOLO model as a starting point, here’s a suggested approach:

  1. Framework Options:
    • PyTorch (recommended since official YOLO models are in PyTorch)
    • You can use YOLO’s architecture but build a simpler training pipeline
  2. Basic Steps:
import torch
from torch import nn
import torch.optim as optim

# 1. Load pretrained model
def load_pretrained_yolo(weights_path='yolov8n-obb.pt'):
    model = torch.load(weights_path)
    return model

# 2. Modify last layer for your needs
def modify_head(model, num_classes):
    # Modify the detection head for your number of classes
    # Exact modification depends on YOLO version and OBB implementation
    pass

# 3. Basic training loop
def train(model, train_loader, val_loader, epochs=100):
    optimizer = optim.AdamW(model.parameters(), lr=0.001)
    scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, epochs)
    
    for epoch in range(epochs):
        model.train()
        for batch in train_loader:
            images, targets = batch
            
            optimizer.zero_grad()
            outputs = model(images)
            loss = compute_loss(outputs, targets)  # You'll need to implement this
            loss.backward()
            optimizer.step()
            
        scheduler.step()

Would you like me to help you build this step by step? We can: 1. First set up the data loading pipeline 2. Then implement the model modification 3. Finally build the training loop

Which part would you like to tackle first?

Remember, key advantages of building your own pipeline: - Full control over the training process - Better understanding of what’s happening - Ability to customize for your specific needs (like your preprocessing) - Can be simpler than full YOLO implementations if you need only specific features