Optimizing satellite image processing with pyvips
Published: 2023-11-27 06:03:45

Developing Payload Applications (PAs) for satellites involves a crucial step of preparing and processing test satellite images. In the wide array of image processing libraries available today, choosing the right one can significantly influence the efficiency and success of your project. 

In this blog, we will explore why pyvips emerged as the best fit for processing satellite images, particularly for our needs, and conclude with a practical example using pyvips to process a JPEG2000 (jp2) format image.

## Obtaining test satellite images
Before diving into image processing, let's briefly touch on how to acquire test satellite images. While this topic is extensively covered in [another blog post](https://dev.to/aykhara/getting-free-satellite-images-for-your-own-payload-app-development-5h2f), it's worth noting that there are several free resources available for obtaining satellite imagery, crucial for testing and development phases of PAs.

## Comparing image processing libraries
When working with satellite images, it's important to choose an image processing library that fits your needs. Let's look at some popular libraries in a simple way and see how they compare:

| Library     | Pros                  | Cons                             | Suitable Use-case                                   |
|-----------------|-------------------------------|-------------------------------------------------|------------------------------------------------------|
| **[OpenCV](https://opencv.org/)**      | Real-time processing, advanced tasks | Can be too complex for simple tasks, uses more resources | Image analysis and processing requiring real-time capabilities and advanced functionality |
| **[Pillow](https://pillow.readthedocs.io/en/stable/)**      | Easy to use, good for basic tasks | Not great for very large or complex images      | Basic image manipulation and simple tasks where ease of use is important |
| **[scikit-image](https://scikit-image.org/)** | Scientific analysis, more complex tasks | Not as efficient for very large images          | Scientific image analysis and more complex image processing tasks           |
| **[pyvips](https://github.com/libvips/pyvips)**      | Large images, efficient processing | Lesser known, less community support           | Handling very large images efficiently, especially in scenarios where performance is critical |

Each library has things it's good at. For example, OpenCV is great if you need to do a lot of different things with your images, but it might be too much for simple tasks. pyvips is really good for big images like the ones from satellites, because it doesn't use a lot of computer memory and works fast. The one you choose depends on what you need for your project, like how big your images are and what you want to do with them.

## Why pyvips for satellite images?

When processing satellite images, pyvips stands out as an excellent choice for a number of compelling reasons:

#### 1. Efficient handling of large images
Satellite images are often very large, posing a significant challenge for many image processing libraries. pyvips is designed for low memory usage, even with large files. It cleverly manages memory by loading images in smaller parts rather than all at once, making it much more efficient for handling these massive images.

#### 2. Speed
In image processing, speed is crucial, especially when dealing with large datasets like satellite images. pyvips processes images faster than many other libraries, an advantage that saves considerable time in both development and execution phases.

#### 3. Support for various formats
pyvips supports a wide range of image formats, including JPEG2000, a popular choice in satellite imagery due to its efficient compression. This versatility is invaluable when working with different types of satellite data.

#### 4. Low-memory reading
One of pyvips' standout features is its ability to read images using minimal memory. This is particularly useful for satellite images, as it allows for the processing of high-resolution images on machines with limited memory resources.

In summary, pyvips' ability to efficiently process large images, its speed, support for multiple formats, and low-memory image reading capabilities make it a top choice for satellite image processing. These features collectively ensure that pyvips is well-equipped to handle the challenges of satellite imagery.

## Implementing pyvips – Sample Code

To work with JPEG2000 format images using pyvips, you need to install it by running the following command:

```bash
$ conda install --channel conda-forge pyvips
```
You can also refer to [the README](https://github.com/libvips/pyvips#install) for more details.


Now, let's dive into some practical applications of pyvips with satellite images. Here is sample code demonstrating various operations:

### Sample Code 1: Resizing and color space conversion of images

Satellite image preprocessing often involves resizing the captured image to a uniform size and converting its color space. In this example, we will demonstrate how to resize the image to 50% of its original size and perform a color space conversion from sRGB to CMYK.

```python
import pyvips

# Load the image from a file
image = pyvips.Image.new_from_file('input.jp2', access='sequential')

# Resize the image
resized_image = image.resize(0.5)  # Resize to 50% of the original size

# Perform a color space conversion (e.g., from sRGB to CMYK)
cmyk_image = resized_image.colourspace("cmyk")

# Save the resized and color-converted image in JPEG2000 format
cmyk_image.jp2ksave('resized_cmyk_image.jp2', lossless=True)
```

- `pyvips.Image.new_from_file()`: Load an image from a file.
- `image.resize()`: Resize the image.
- `image.colourspace()`: Perform a color space conversion.
- `image.jp2ksave()`: Save the image in JPEG2000 format.

### Sample Code 2: Rotation and monochrome conversion of images

Depending on the angle and orientation of satellite imagery, it might be necessary to rotate the image or convert it to monochrome for processing in PAs. This example will introduce how to rotate the image by 90 degrees and convert the rotated image to monochrome (black and white).

```python
import pyvips

# Load the image from a file
image = pyvips.Image.new_from_file('input.jp2', access='sequential')

# Rotate the image by 90 degrees
rotated_image = image.rot90()

# Convert the image to monochrome
monochrome_image = rotated_image.colourspace("b-w")

# Save the rotated and monochrome image in JPEG2000 format
monochrome_image.jp2ksave('rotated_monochrome_image.jp2')
```

- `image.rot90()`: Rotate the image by 90 degrees.

### Sample Code 3: Image splitting

By splitting the image into smaller segments, we can process large images more effectively in machine learning models, potentially improving accuracy in tasks like image recognition. In this example, we will demonstrate how to calculate the division of the loaded image into a specified number of segments, in this case, 7 x 7, and how to save these divided images.

```python
import pyvips

# Load the image from a file
image = pyvips.Image.new_from_file('input.jp2', access='sequential')

# Define the number of segments per axis (7x7 grid)
num_segments = 7

# Calculate segment dimensions
segment_width = image.width // num_segments
segment_height = image.height // num_segments

# Loop through the grid and save each segment
for row in range(num_segments):
    for col in range(num_segments):
        # Calculate the position of the current segment
        left = col * segment_width
        top = row * segment_height

        # Extract the segment
        segment = image.crop(left, top, segment_width, segment_height)

        # Save the segment
        segment.jp2ksave(f'segment_{row}_{col}.jp2')
```

- `image.width` and `image.height` (Properties of pyvips Image Object): Retrieve the width and height of the loaded image.
- `image.crop`: Extracts a specified portion of the image based on given coordinates and dimensions.

### Sample Code 4: Adding an alpha channel

Due to the hardware of some satellites, the captured images may include a fourth dimension, such as an alpha channel. This example will show how to add an alpha channel to the loaded image and verify the change in the number of channels before and after processing.

```python
import pyvips

# Load the image from a file
image = pyvips.Image.new_from_file('input.jp2', access='sequential')

# Print the current number of bands in the image
print(f"Original number of bands: {image.bands}")

# Add an alpha channel to the image
image_with_alpha = image.addalpha()

# Save the image with the alpha channel in JPEG2000 format
image_with_alpha.jp2ksave('image_with_alpha.jp2')

# Reload the saved image to verify the band count
pyvips.Image.new_from_file('image_with_alpha.jp2')

# Print the number of bands in the modified image
modified_image = pyvips.Image.new_from_file('image_with_alpha.jp2', access='sequential')
print(f"Modified number of bands: {modified_image.bands}")
```

- `image.bands` (Property of pyvips Image Object): Returns the number of bands (channels) in the image.
- `image.addalpha()`: Adds an alpha channel to the image for transparency.

These examples showcase the capability of pyvips to handle complex image processing tasks efficiently, making it a valuable tool for working with high-resolution satellite imagery.

## Conclusion
When making payload applications for satellites, picking the right tool to process your images is really important. pyvips is a really useful tool for anyone working with satellite images, whether you're just starting out or have lots of experience.