mkenfenheuer/rvl-cdip-restoration
RVL-CDIP Restoration A document image restoration dataset based on the original RVL-CDIP dataset. Original dataset: - https://huggingface.co/datasets/ChainYo/rvl-cdip This version converts the classification dataset into a supervised image-to-image restoration dataset. Each sample contains: - lr → degraded document (input) - hr → original clean document (target) Images are stored as: - raw numpy bytes - dtype: uint8 - shape: (512, 512, 3) Dataset Overview This… See the full description on the dataset page: https://huggingface.co/datasets/mkenfenheuer/rvl-cdip-restoration.
RVL-CDIP Restoration
A document image restoration dataset based on the original RVL-CDIP dataset.
Original dataset: - https://huggingface.co/datasets/ChainYo/rvl-cdip
This version converts the classification dataset into a supervised image-to-image restoration dataset.
Each sample contains: - lr → degraded document (input) - hr → original clean document (target)
Images are stored as: - raw numpy bytes - dtype: uint8 - shape: (512, 512, 3)
Dataset Overview
This dataset is derived from the RVL-CDIP document classification dataset and reformulated for supervised document restoration tasks.
Instead of classification labels, each sample now contains a paired image:
Both are stored as raw NumPy byte arrays to minimize storage overhead and maximize loading speed.
Image Format
Each image:
- Resolution: 512×512
- Channels: 3 (RGB)
- Datatype: uint8
- Storage: raw bytes
To convert into tensors:
from datasets import load_dataset
import numpy as np
import torch
ds = load_dataset("mkenfenheuer/rvl-cdip-restoration", split="train")
PATCH_SIZE = 512
H, W, C = PATCH_SIZE, PATCH_SIZE, 3
sample = ds[0]
# Convert bytes → numpy
lr_np = np.frombuffer(sample["lr"], dtype=np.uint8).reshape(H, W, C)
hr_np = np.frombuffer(sample["hr"], dtype=np.uint8).reshape(H, W, C)
# Convert numpy → torch tensor [C,H,W] in [0,1]
lr_tensor = torch.from_numpy(lr_np).permute(2,0,1).float() / 255.0
hr_tensor = torch.from_numpy(hr_np).permute(2,0,1).float() / 255.0After conversion:
- Shape:
[C, H, W] - Range:
[0,1] - dtype:
float32
Task Definition
Supervised image restoration:
model(lr) → hr
Where: - lr = degraded input document - hr = clean ground truth
Recommended Loss Functions
- L1 Loss
- SSIM / MS-SSIM
- Perceptual Loss (VGG-based)
- Combined L1 + SSIM
Example:
loss = 0.8 * L1(pred, hr) + 0.2 * (1 - SSIM(pred, hr))Example Training Script
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from datasets import load_dataset
import numpy as np
DEVICE = "cuda"
PATCH_SIZE = 512
H, W, C = PATCH_SIZE, PATCH_SIZE, 3
BATCH_SIZE = 4
ds = load_dataset("mkenfenheuer/rvl-cdip-restoration", split="train")
def collate_fn(batch):
lr_list = []
hr_list = []
for sample in batch:
lr_np = np.frombuffer(sample["lr"], dtype=np.uint8).reshape(H,W,C)
hr_np = np.frombuffer(sample["hr"], dtype=np.uint8).reshape(H,W,C)
lr_tensor = torch.from_numpy(lr_np).permute(2,0,1).float() / 255.0
hr_tensor = torch.from_numpy(hr_np).permute(2,0,1).float() / 255.0
lr_list.append(lr_tensor)
hr_list.append(hr_tensor)
return {
"lr": torch.stack(lr_list),
"hr": torch.stack(hr_list)
}
loader = DataLoader(ds, batch_size=BATCH_SIZE, shuffle=True, collate_fn=collate_fn)
model = torch.nn.Conv2d(3,3,3,padding=1).to(DEVICE)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
criterion = nn.L1Loss()
for epoch in range(10):
model.train()
for batch in loader:
lr = batch["lr"].to(DEVICE)
hr = batch["hr"].to(DEVICE)
pred = model(lr)
loss = criterion(pred, hr)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch {epoch} | Loss: {loss.item():.4f}")Example Inference Script
import torch
import matplotlib.pyplot as plt
import numpy as np
from datasets import load_dataset
DEVICE = "cuda"
PATCH_SIZE = 512
H, W, C = PATCH_SIZE, PATCH_SIZE, 3
ds = load_dataset("mkenfenheuer/rvl-cdip-restoration", split="val")
model = torch.load("model.pt").to(DEVICE)
model.eval()
sample = ds[0]
lr_np = np.frombuffer(sample["lr"], dtype=np.uint8).reshape(H, W, C)
hr_np = np.frombuffer(sample["hr"], dtype=np.uint8).reshape(H, W, C)
lr_tensor = torch.from_numpy(lr_np).permute(2,0,1).float() / 255.0
hr_tensor = torch.from_numpy(hr_np).permute(2,0,1).float() / 255.0
lr_tensor = lr_tensor.unsqueeze(0).to(DEVICE)
with torch.no_grad(), torch.amp.autocast(device_type="cuda"):
restored_tensor = model(lr_tensor)
def tensor_to_rgb_np(t):
if t.dim() == 4:
t = t.squeeze(0)
np_img = t.permute(1,2,0).cpu().numpy()
np_img = np.clip(np_img, 0.0, 1.0)
return np_img.astype(np.float32)
lr_np = tensor_to_rgb_np(lr_tensor)
restored_np = tensor_to_rgb_np(restored_tensor)
fig, axes = plt.subplots(1, 2, figsize=(8,4))
axes[0].imshow(lr_np)
axes[0].set_title("LR")
axes[1].imshow(restored_np)
axes[1].set_title("Restored")
for ax in axes:
ax.axis("off")
plt.tight_layout()
plt.show()Citation
@inproceedings{harley2015evaluation, title={Evaluation of Deep Convolutional Nets for Document Image Classification and Retrieval}, author={Harley, Adam W. and Ufkes, Alex and Derpanis, Konstantinos G.}, booktitle={ICDAR}, year={2015} }
License
RVL-CDIP is a subset of IIT-CDIP, which came from the Legacy Tobacco Document Library, for which license information can be found here.
