Skip to main content

Image Sharpening with LatticaAI Demo Tutorial

An image is encrypted on the client, processed by the protected image-sharpening model, then decrypted on the client as a sharpened image.
Client side
Original imagePlaintext stays here
Encrypt input
Server side
Image sharpeningEncrypted computation
Decrypt result
Client side
Sharpened imageReadable only here

Overview of the Model​

Our Image Sharpening model enhances the clarity and detail of an input image by applying a specialized 2D filter through convolution.

  • Input Format: RGB image tensor of shape (3, 100, 100), with pixel values in the [0,1] range.
  • Output: Sharpened image preserving original dimensions.

The equivalent pytorch code for the operator is:

import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt

# Load image as numpy ndarray (ignore alpha channel if it exists)
np_img = plt.imread('house.png')[..., :3] # shape format (H, W, C)
assert np_img.shape[-1] == 3, "Image must have 3 channels"

# Convert to PyTorch tensor and arrange dimensions as (C, H, W)
pt = torch.tensor(np_img, dtype=torch.float32).permute(2, 0, 1)
if pt.max() > 1:
pt /= 255

# Resize image to expected input size
pt = F.interpolate(pt.unsqueeze(0), size=(100, 100), mode='bilinear').squeeze(0) # shape (3, 100, 100)

# Define sharpening kernel
sharpen_kernel = torch.tensor([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], dtype=torch.float)

# Apply convolution for each channel separately (3 for RGB)
res = F.conv2d(pt, sharpen_kernel.expand(3, 1, 3, 3), groups=3, padding=1)
# Clamp to range [0, 1]
res = torch.clamp(res, 0, 1)

# Display the original and sharpened images
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Original Image")
plt.imshow(pt.permute(1, 2, 0))
plt.axis("off")

plt.subplot(1, 2, 2)
plt.title("Sharpened Image")
plt.imshow(res.permute(1, 2, 0))
plt.axis("off")

plt.show()

Achieving Full Privacy with LatticaAI​

This demo uses normalized image pixels and a channel-wise sharpening convolution. The query client encrypts the input tensor locally before sending it to the worker.

In order to convert this simple code to use homomorphic operations, all you need are the following few extra steps:

  1. Install the Lattica query package and obtain a query token.
  2. Generate or reuse your encryption keys.
  3. Call the encrypted-query API, which preprocesses and encrypts the image locally, sends only ciphertext to the worker, and decrypts the encrypted result locally.

Everything else remains the same.

First install our client package

import os

from lattica_query import QueryClient, QueryToken

# Set LATTICA_QUERY_TOKEN to a query token scoped to this demo model.
token = QueryToken(os.environ["LATTICA_QUERY_TOKEN"])
with QueryClient(token) as client:
key = client.keys.ensure()
# `pt` and `res` are torch.Tensor objects, as in the plain example above.
res = client.query.encrypted(pt, key=key)

# Display the original and sharpened images...

See our step-by-step guide for a detailed explanation of each step in this flow.
To use the image sharpening model, use the imageEnhancement model ID.