220 lines
5.3 KiB
Markdown
220 lines
5.3 KiB
Markdown
|
|
# Image Class Usage Guide
|
||
|
|
|
||
|
|
The `Image` class provides a convenient way to load and work with images in the microscopy object detection application.
|
||
|
|
|
||
|
|
## Supported Formats
|
||
|
|
|
||
|
|
The Image class supports the following image formats:
|
||
|
|
- `.jpg`, `.jpeg` - JPEG images
|
||
|
|
- `.png` - PNG images
|
||
|
|
- `.tif`, `.tiff` - TIFF images (commonly used in microscopy)
|
||
|
|
- `.bmp` - Bitmap images
|
||
|
|
|
||
|
|
## Basic Usage
|
||
|
|
|
||
|
|
### Loading an Image
|
||
|
|
|
||
|
|
```python
|
||
|
|
from src.utils import Image, ImageLoadError
|
||
|
|
|
||
|
|
# Load an image from a file path
|
||
|
|
try:
|
||
|
|
img = Image("path/to/image.jpg")
|
||
|
|
print(f"Loaded image: {img.width}x{img.height} pixels")
|
||
|
|
except ImageLoadError as e:
|
||
|
|
print(f"Failed to load image: {e}")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Accessing Image Properties
|
||
|
|
|
||
|
|
```python
|
||
|
|
img = Image("microscopy_image.tif")
|
||
|
|
|
||
|
|
# Basic properties
|
||
|
|
print(f"Width: {img.width} pixels")
|
||
|
|
print(f"Height: {img.height} pixels")
|
||
|
|
print(f"Channels: {img.channels}")
|
||
|
|
print(f"Format: {img.format}")
|
||
|
|
print(f"Shape: {img.shape}") # (height, width, channels)
|
||
|
|
|
||
|
|
# File information
|
||
|
|
print(f"File size: {img.size_mb:.2f} MB")
|
||
|
|
print(f"File size: {img.size_bytes} bytes")
|
||
|
|
|
||
|
|
# Image type checks
|
||
|
|
print(f"Is color: {img.is_color()}")
|
||
|
|
print(f"Is grayscale: {img.is_grayscale()}")
|
||
|
|
|
||
|
|
# String representation
|
||
|
|
print(img) # Shows summary of image properties
|
||
|
|
```
|
||
|
|
|
||
|
|
### Working with Image Data
|
||
|
|
|
||
|
|
```python
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
img = Image("sample.png")
|
||
|
|
|
||
|
|
# Get image data as numpy array (OpenCV format, BGR)
|
||
|
|
bgr_data = img.data
|
||
|
|
print(f"Data shape: {bgr_data.shape}")
|
||
|
|
print(f"Data type: {bgr_data.dtype}")
|
||
|
|
|
||
|
|
# Get image as RGB (for display or processing)
|
||
|
|
rgb_data = img.get_rgb()
|
||
|
|
|
||
|
|
# Get grayscale version
|
||
|
|
gray_data = img.get_grayscale()
|
||
|
|
|
||
|
|
# Create a copy (for modifications)
|
||
|
|
img_copy = img.copy()
|
||
|
|
img_copy[0, 0] = [255, 255, 255] # Modify copy, original unchanged
|
||
|
|
|
||
|
|
# Resize image (returns new array, doesn't modify original)
|
||
|
|
resized = img.resize(640, 640)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Using PIL Image
|
||
|
|
|
||
|
|
```python
|
||
|
|
img = Image("photo.jpg")
|
||
|
|
|
||
|
|
# Access as PIL Image (RGB format)
|
||
|
|
pil_img = img.pil_image
|
||
|
|
|
||
|
|
# Use PIL methods
|
||
|
|
pil_img.show() # Display image
|
||
|
|
pil_img.save("output.png") # Save with PIL
|
||
|
|
```
|
||
|
|
|
||
|
|
## Integration with YOLO
|
||
|
|
|
||
|
|
```python
|
||
|
|
from src.utils import Image
|
||
|
|
from ultralytics import YOLO
|
||
|
|
|
||
|
|
# Load model and image
|
||
|
|
model = YOLO("yolov8n.pt")
|
||
|
|
img = Image("microscopy/cell_01.tif")
|
||
|
|
|
||
|
|
# Run inference (YOLO accepts file paths or numpy arrays)
|
||
|
|
results = model(img.data)
|
||
|
|
|
||
|
|
# Or use the file path directly
|
||
|
|
results = model(str(img.path))
|
||
|
|
```
|
||
|
|
|
||
|
|
## Error Handling
|
||
|
|
|
||
|
|
```python
|
||
|
|
from src.utils import Image, ImageLoadError
|
||
|
|
|
||
|
|
def process_image(image_path):
|
||
|
|
try:
|
||
|
|
img = Image(image_path)
|
||
|
|
# Process the image...
|
||
|
|
return img
|
||
|
|
except ImageLoadError as e:
|
||
|
|
print(f"Cannot load image: {e}")
|
||
|
|
return None
|
||
|
|
```
|
||
|
|
|
||
|
|
## Advanced Usage
|
||
|
|
|
||
|
|
### Batch Processing
|
||
|
|
|
||
|
|
```python
|
||
|
|
from pathlib import Path
|
||
|
|
from src.utils import Image, ImageLoadError
|
||
|
|
|
||
|
|
def process_image_directory(directory):
|
||
|
|
"""Process all images in a directory."""
|
||
|
|
image_paths = Path(directory).glob("*.tif")
|
||
|
|
|
||
|
|
for path in image_paths:
|
||
|
|
try:
|
||
|
|
img = Image(path)
|
||
|
|
print(f"Processing {img.path.name}: {img.width}x{img.height}")
|
||
|
|
# Process the image...
|
||
|
|
except ImageLoadError as e:
|
||
|
|
print(f"Skipping {path}: {e}")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Using with OpenCV Operations
|
||
|
|
|
||
|
|
```python
|
||
|
|
import cv2
|
||
|
|
from src.utils import Image
|
||
|
|
|
||
|
|
img = Image("input.jpg")
|
||
|
|
|
||
|
|
# Apply OpenCV operations on the data
|
||
|
|
blurred = cv2.GaussianBlur(img.data, (5, 5), 0)
|
||
|
|
edges = cv2.Canny(img.data, 100, 200)
|
||
|
|
|
||
|
|
# Note: These operations don't modify the original img.data
|
||
|
|
```
|
||
|
|
|
||
|
|
### Memory Efficient Processing
|
||
|
|
|
||
|
|
```python
|
||
|
|
from src.utils import Image
|
||
|
|
|
||
|
|
# The Image class loads data into memory
|
||
|
|
img = Image("large_image.tif")
|
||
|
|
print(f"Image size in memory: {img.data.nbytes / (1024**2):.2f} MB")
|
||
|
|
|
||
|
|
# When processing many images, consider loading one at a time
|
||
|
|
# and releasing memory by deleting the object
|
||
|
|
del img
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
1. **Always use try-except** when loading images to handle errors gracefully
|
||
|
|
2. **Check image properties** before processing to ensure compatibility
|
||
|
|
3. **Use copy()** when you need to modify image data without affecting the original
|
||
|
|
4. **Path objects work too** - The class accepts both strings and Path objects
|
||
|
|
5. **Consider memory usage** when working with large images or batches
|
||
|
|
|
||
|
|
## Example: Complete Workflow
|
||
|
|
|
||
|
|
```python
|
||
|
|
from src.utils import Image, ImageLoadError
|
||
|
|
from src.utils.file_utils import get_image_files
|
||
|
|
|
||
|
|
def analyze_microscopy_images(directory):
|
||
|
|
"""Analyze all microscopy images in a directory."""
|
||
|
|
|
||
|
|
# Get all image files
|
||
|
|
image_files = get_image_files(directory, recursive=True)
|
||
|
|
|
||
|
|
results = []
|
||
|
|
for image_path in image_files:
|
||
|
|
try:
|
||
|
|
# Load image
|
||
|
|
img = Image(image_path)
|
||
|
|
|
||
|
|
# Analyze
|
||
|
|
result = {
|
||
|
|
'filename': img.path.name,
|
||
|
|
'width': img.width,
|
||
|
|
'height': img.height,
|
||
|
|
'channels': img.channels,
|
||
|
|
'format': img.format,
|
||
|
|
'size_mb': img.size_mb,
|
||
|
|
'is_color': img.is_color()
|
||
|
|
}
|
||
|
|
|
||
|
|
results.append(result)
|
||
|
|
print(f"✓ Analyzed {img.path.name}")
|
||
|
|
|
||
|
|
except ImageLoadError as e:
|
||
|
|
print(f"✗ Failed to load {image_path}: {e}")
|
||
|
|
|
||
|
|
return results
|
||
|
|
|
||
|
|
# Run analysis
|
||
|
|
results = analyze_microscopy_images("data/datasets/cells")
|
||
|
|
print(f"\nProcessed {len(results)} images")
|