295 lines
13 KiB
Plaintext
295 lines
13 KiB
Plaintext
---
|
|
description: This rule provides guidelines for best practices and coding standards when using the scikit-image library for image processing in Python. It covers code organization, performance, security, testing, and common pitfalls.
|
|
globs: *.py
|
|
---
|
|
- Always use UV when installing dependencies for faster and more deterministic dependency resolution.
|
|
- Always use Python 3.12 or later to leverage the latest language features and performance improvements.
|
|
- Utilize classes instead of standalone functions where appropriate for better code organization and encapsulation, especially when dealing with stateful image processing operations.
|
|
|
|
## Scikit-image Best Practices and Coding Standards
|
|
|
|
This document outlines the best practices and coding standards for using the scikit-image library in Python for image processing. Following these guidelines will help ensure code clarity, maintainability, performance, and security.
|
|
|
|
### Library Information:
|
|
- Name: scikit-image
|
|
- Tags: python, image-processing, scientific-computing
|
|
|
|
### 1. Code Organization and Structure:
|
|
|
|
- **Directory Structure:**
|
|
- Adopt a modular directory structure to organize your scikit-image projects.
|
|
- Example:
|
|
|
|
project_name/
|
|
├── data/ # Contains image data (e.g., input images, sample datasets)
|
|
├── src/ # Source code directory
|
|
│ ├── __init__.py # Marks src as a Python package
|
|
│ ├── modules/
|
|
│ │ ├── __init__.py
|
|
│ │ ├── image_io.py # Image input/output related functions
|
|
│ │ ├── processing.py # Core image processing algorithms
|
|
│ │ ├── segmentation.py # Segmentation algorithms
|
|
│ │ └── feature.py # Feature extraction modules
|
|
│ ├── utils.py # Utility functions
|
|
│ └── main.py # Main application entry point
|
|
├── tests/ # Unit and integration tests
|
|
│ ├── __init__.py
|
|
│ ├── test_image_io.py
|
|
│ ├── test_processing.py
|
|
│ └── test_segmentation.py
|
|
├── notebooks/ # Jupyter notebooks for exploration
|
|
├── requirements.txt # Project dependencies
|
|
├── pyproject.toml # Project metadata and build system
|
|
└── README.md # Project documentation
|
|
|
|
|
|
- **File Naming Conventions:**
|
|
- Use descriptive and consistent file names.
|
|
- Module files: `image_io.py`, `processing.py`, `segmentation.py`
|
|
- Test files: `test_image_io.py`, `test_processing.py`
|
|
- Utility files: `utils.py`
|
|
|
|
- **Module Organization:**
|
|
- Group related functions and classes into modules.
|
|
- Avoid monolithic modules; break down large modules into smaller, more manageable ones.
|
|
- Use `__init__.py` files to define packages and control namespace exposure.
|
|
- Example (in `src/modules/processing.py`):
|
|
python
|
|
from skimage import filters
|
|
from skimage import morphology
|
|
import numpy as np
|
|
|
|
def apply_threshold(image, threshold_value=128):
|
|
"""Applies a simple threshold to an image."""
|
|
return image > threshold_value
|
|
|
|
def remove_small_objects(binary_image, min_size=100):
|
|
"""Removes small connected components from a binary image."""
|
|
return morphology.remove_small_objects(binary_image, min_size=min_size)
|
|
|
|
|
|
- **Component Architecture:**
|
|
- Design components with clear responsibilities and well-defined interfaces.
|
|
- Favor composition over inheritance for greater flexibility.
|
|
- Use abstract base classes (ABCs) to define interfaces for components.
|
|
- Example:
|
|
python
|
|
from abc import ABC, abstractmethod
|
|
|
|
class ImageProcessor(ABC):
|
|
@abstractmethod
|
|
def process_image(self, image):
|
|
pass
|
|
|
|
class GrayscaleConverter(ImageProcessor):
|
|
def process_image(self, image):
|
|
from skimage.color import rgb2gray
|
|
return rgb2gray(image)
|
|
|
|
|
|
- **Code Splitting Strategies:**
|
|
- Decompose complex image processing pipelines into smaller, reusable functions.
|
|
- Use generators or iterators for processing large images in chunks.
|
|
- Consider using multiprocessing or multithreading for parallel processing of image regions.
|
|
- Utilize lazy loading techniques for large image datasets.
|
|
|
|
### 2. Common Patterns and Anti-patterns:
|
|
|
|
- **Design Patterns:**
|
|
- **Factory Pattern:** Use factory functions or classes to create image processing objects.
|
|
- **Strategy Pattern:** Implement different image processing algorithms as strategies that can be swapped at runtime.
|
|
- **Observer Pattern:** Notify observers when an image processing operation completes.
|
|
|
|
- **Recommended Approaches:**
|
|
- Use NumPy arrays as the primary data structure for image representation.
|
|
- Leverage scikit-image's functional API for modular and composable image processing pipelines.
|
|
- Use `img_as_float` or other data type conversion utilities to ensure consistent data types.
|
|
- Document image processing functions and classes using docstrings.
|
|
|
|
- **Anti-patterns and Code Smells:**
|
|
- **Global State:** Avoid using global variables to store image data or processing parameters.
|
|
- **Magic Numbers:** Use named constants instead of hardcoded numerical values.
|
|
- **Deeply Nested Loops:** Optimize image processing loops using NumPy's vectorized operations.
|
|
- **Ignoring Data Types:** Always be aware of the data types of images and intermediate results.
|
|
- **Over-Complicating Code:** Aim for simplicity and readability in your image processing code.
|
|
|
|
- **State Management:**
|
|
- Encapsulate state within classes or data structures.
|
|
- Use immutable data structures where possible.
|
|
- Avoid modifying image data in-place unless necessary.
|
|
|
|
- **Error Handling:**
|
|
- Use try-except blocks to handle potential exceptions (e.g., file I/O errors, invalid image formats).
|
|
- Log errors and warnings using the `logging` module.
|
|
- Provide informative error messages to the user.
|
|
- Consider custom exception types for scikit-image related errors.
|
|
- Example:
|
|
python
|
|
import logging
|
|
from skimage import io
|
|
|
|
logging.basicConfig(level=logging.ERROR)
|
|
|
|
def load_image(filepath):
|
|
try:
|
|
image = io.imread(filepath)
|
|
return image
|
|
except FileNotFoundError:
|
|
logging.error(f"File not found: {filepath}")
|
|
return None
|
|
except Exception as e:
|
|
logging.exception(f"Error loading image: {e}")
|
|
return None
|
|
|
|
|
|
### 3. Performance Considerations:
|
|
|
|
- **Optimization Techniques:**
|
|
- Vectorize image processing operations using NumPy's broadcasting and array manipulation features.
|
|
- Use Cython to optimize performance-critical sections of code.
|
|
- Explore Numba for just-in-time (JIT) compilation of image processing functions.
|
|
- Utilize `skimage.util.apply_parallel` for parallel processing of image regions.
|
|
- Example using NumPy vectorization:
|
|
python
|
|
import numpy as np
|
|
|
|
def brighten_image(image, factor=1.5):
|
|
"""Brightens an image by multiplying each pixel by a factor."""
|
|
return np.clip(image * factor, 0, 255).astype(image.dtype) # Clip values to valid range
|
|
|
|
|
|
- **Memory Management:**
|
|
- Use appropriate data types to minimize memory usage (e.g., `uint8` for grayscale images).
|
|
- Release large image arrays when they are no longer needed.
|
|
- Avoid creating unnecessary copies of image data.
|
|
- Consider using memory-mapped arrays for very large images.
|
|
|
|
- **Rendering Optimization:**
|
|
- Optimize image display using appropriate colormaps and scaling.
|
|
- Use hardware acceleration (e.g., OpenGL) for faster rendering.
|
|
|
|
- **Bundle Size Optimization:**
|
|
- Minimize dependencies in your scikit-image projects.
|
|
- Use tree shaking to remove unused code from your bundles.
|
|
- Compress image assets using appropriate compression algorithms.
|
|
|
|
- **Lazy Loading:**
|
|
- Load large images only when they are needed.
|
|
- Use generators or iterators to process images in chunks.
|
|
- Implement caching mechanisms to avoid redundant image loading.
|
|
|
|
### 4. Security Best Practices:
|
|
|
|
- **Common Vulnerabilities:**
|
|
- **Denial-of-Service (DoS) Attacks:** Protect against DoS attacks by limiting the size of input images.
|
|
- **Code Injection:** Sanitize user-provided image processing parameters to prevent code injection attacks.
|
|
|
|
- **Input Validation:**
|
|
- Validate the format, size, and data type of input images.
|
|
- Check for malicious image headers or metadata.
|
|
- Sanitize user-provided parameters to prevent code injection.
|
|
- Example:
|
|
python
|
|
from skimage import io
|
|
|
|
def process_image(filepath, resize_factor):
|
|
if not isinstance(resize_factor, (int, float)):
|
|
raise ValueError("Resize factor must be a number.")
|
|
if resize_factor <= 0:
|
|
raise ValueError("Resize factor must be positive.")
|
|
|
|
try:
|
|
image = io.imread(filepath)
|
|
# Perform image processing operations using resize_factor
|
|
except Exception as e:
|
|
print(f"Error processing image: {e}")
|
|
|
|
|
|
- **Authentication and Authorization:**
|
|
- Implement authentication and authorization mechanisms to control access to image processing resources.
|
|
- Use secure protocols (e.g., HTTPS) for API communication.
|
|
|
|
- **Data Protection:**
|
|
- Encrypt sensitive image data at rest and in transit.
|
|
- Implement access control policies to protect image data.
|
|
- Use secure storage mechanisms for image data.
|
|
|
|
- **Secure API Communication:**
|
|
- Use HTTPS for all API communication.
|
|
- Implement rate limiting to prevent abuse.
|
|
- Use input validation to prevent injection attacks.
|
|
|
|
### 5. Testing Approaches:
|
|
|
|
- **Unit Testing:**
|
|
- Write unit tests for individual functions and classes.
|
|
- Use mocking and stubbing to isolate components during testing.
|
|
- Test edge cases and boundary conditions.
|
|
|
|
- **Integration Testing:**
|
|
- Write integration tests to verify the interaction between components.
|
|
- Test complex image processing pipelines.
|
|
- Use realistic test data.
|
|
|
|
- **End-to-End Testing:**
|
|
- Write end-to-end tests to verify the entire application workflow.
|
|
- Use automated testing frameworks (e.g., Selenium).
|
|
|
|
- **Test Organization:**
|
|
- Organize tests into separate directories for unit, integration, and end-to-end tests.
|
|
- Use descriptive test names.
|
|
- Follow a consistent testing style.
|
|
|
|
- **Mocking and Stubbing:**
|
|
- Use mocking libraries (e.g., `unittest.mock`) to replace external dependencies with mock objects.
|
|
- Use stubbing to provide predefined outputs for specific function calls.
|
|
|
|
### 6. Common Pitfalls and Gotchas:
|
|
|
|
- **Data Type Issues:**
|
|
- Be aware of the data types of images and intermediate results.
|
|
- Use `img_as_float` or other data type conversion utilities to ensure consistent data types.
|
|
- Pay attention to data type ranges (e.g., 0-255 for `uint8`, 0.0-1.0 for float).
|
|
|
|
- **Coordinate Conventions:**
|
|
- Understand the coordinate conventions used by scikit-image (row, col) and NumPy.
|
|
|
|
- **Memory Consumption:**
|
|
- Avoid creating unnecessary copies of image data.
|
|
- Process large images in chunks or tiles.
|
|
|
|
- **Version Compatibility:**
|
|
- Be aware of version-specific API changes and deprecations.
|
|
- Check scikit-image's changelog for breaking changes.
|
|
|
|
- **Image I/O Issues:**
|
|
- Use appropriate image formats for your application.
|
|
- Handle file I/O errors gracefully.
|
|
|
|
### 7. Tooling and Environment:
|
|
|
|
- **Recommended Development Tools:**
|
|
- IDE: VS Code, PyCharm
|
|
- Debugger: pdb, ipdb
|
|
- Profiler: cProfile, line_profiler
|
|
|
|
- **Build Configuration:**
|
|
- Use `pyproject.toml` to manage project metadata and build dependencies.
|
|
- Use `requirements.txt` to specify project dependencies.
|
|
|
|
- **Linting and Formatting:**
|
|
- Use a linter (e.g., flake8, pylint) to enforce coding style and detect errors.
|
|
- Use a formatter (e.g., black, autopep8) to automatically format code.
|
|
- Configure your IDE to run linters and formatters automatically.
|
|
|
|
- **Deployment:**
|
|
- Use virtual environments to isolate project dependencies.
|
|
- Containerize your scikit-image applications using Docker.
|
|
- Deploy your applications to cloud platforms (e.g., AWS, Azure, GCP).
|
|
|
|
- **CI/CD Integration:**
|
|
- Use a CI/CD pipeline (e.g., GitHub Actions, GitLab CI, Jenkins) to automate testing, building, and deployment.
|
|
- Run linters, formatters, and tests in your CI/CD pipeline.
|
|
- Use code coverage tools to measure the effectiveness of your tests.
|
|
|
|
By adhering to these best practices, you can develop robust, maintainable, and performant image processing applications using the scikit-image library. |