-
Notifications
You must be signed in to change notification settings - Fork 578
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use pyvips for image resizing (#185)
* perf: replace PIL with pyvips for 3.3x faster image processing - Replace PIL with pyvips in image_crops.py for faster image resizing and cropping - Add pyvips dependency to requirements.txt - Benchmark shows 3.3x speedup with identical output quality - Note: requires libvips system package * Use pyvips-binary instead of system libvips * Add tests for image_crops and GitHub workflow * Apply black formatting * Use improved numpy integration from pyvips 2.2 --------- Co-authored-by: openhands <openhands@all-hands.dev>
- Loading branch information
1 parent
35995b7
commit 9ceccdb
Showing
4 changed files
with
102 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
name: Tests | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: ["3.9", "3.10", "3.11", "3.12"] | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install pytest | ||
pip install -r requirements.txt | ||
- name: Run tests | ||
run: | | ||
python -m pytest tests/test_image_crops.py -v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import numpy as np | ||
import torch | ||
from moondream.torch.image_crops import overlap_crop_image, reconstruct_from_crops | ||
|
||
|
||
def test_overlap_crop_basic(): | ||
# Create a test image | ||
test_image = np.zeros((800, 600, 3), dtype=np.uint8) | ||
# Add a recognizable pattern - white rectangle in the middle | ||
test_image[300:500, 200:400] = 255 | ||
|
||
result = overlap_crop_image(test_image, overlap_margin=4, max_crops=12) | ||
|
||
# Check basic properties | ||
assert result["global_crop"].shape == (378, 378, 3) | ||
assert len(result["local_crops"]) > 0 | ||
assert all(crop.shape == (378, 378, 3) for crop in result["local_crops"]) | ||
assert len(result["tiling"]) == 2 | ||
|
||
|
||
def test_overlap_crop_small_image(): | ||
# Test with image smaller than crop size | ||
test_image = np.zeros((300, 200, 3), dtype=np.uint8) | ||
result = overlap_crop_image(test_image, overlap_margin=4, max_crops=12) | ||
|
||
# Should still produce valid output | ||
assert result["global_crop"].shape == (378, 378, 3) | ||
assert len(result["local_crops"]) == 1 | ||
assert result["tiling"] == (1, 1) | ||
|
||
|
||
def test_reconstruction(): | ||
# Create a test image | ||
test_image = np.zeros((800, 600, 3), dtype=np.uint8) | ||
# Add a recognizable pattern | ||
test_image[300:500, 200:400] = 255 | ||
|
||
# Crop and reconstruct | ||
result = overlap_crop_image(test_image, overlap_margin=4, max_crops=12) | ||
crops_tensor = [torch.from_numpy(crop) for crop in result["local_crops"]] | ||
reconstructed = reconstruct_from_crops( | ||
crops_tensor, result["tiling"], overlap_margin=4 | ||
) | ||
|
||
# Convert back to numpy for comparison | ||
reconstructed_np = reconstructed.numpy() | ||
|
||
# The reconstructed image should be similar to the input | ||
# We can't expect exact equality due to resizing operations | ||
# but the white rectangle should still be visible in the middle | ||
center_original = test_image[300:500, 200:400].mean() | ||
center_reconstructed = reconstructed_np[ | ||
reconstructed_np.shape[0] // 2 - 100 : reconstructed_np.shape[0] // 2 + 100, | ||
reconstructed_np.shape[1] // 2 - 100 : reconstructed_np.shape[1] // 2 + 100, | ||
].mean() | ||
|
||
# The center region should be significantly brighter than the edges | ||
assert center_reconstructed > reconstructed_np[:100, :100].mean() + 100 |