Skip to content

Commit

Permalink
🎉 Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
DelinQu committed Jul 22, 2023
1 parent 6c3bc67 commit b07530b
Show file tree
Hide file tree
Showing 19 changed files with 340 additions and 86 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

# Created by https://www.gitignore.io/api/osx,python,pycharm,windows,visualstudio,visualstudiocode
# Edit at https://www.gitignore.io/?templates=osx,python,pycharm,windows,visualstudio,visualstudiocode
./scripts

### OSX ###
# General
Expand Down
77 changes: 14 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

<div align="center">

[![Build status](https://github.com/rspy/rspy/workflows/build/badge.svg?branch=master&event=push)](https://github.com/rspy/rspy/actions?query=workflow%3Abuild)
[![Python Version](https://img.shields.io/pypi/pyversions/rspy.svg)](https://pypi.org/project/rspy/)
[![Python Version](https://img.shields.io/badge/python-3.8-blue
)](https://img.shields.io/badge/python-3.9-pink) [![Python Version](https://img.shields.io/badge/python-3.9-pink
)](https://img.shields.io/badge/python-3.8-blue) [![Python Version](https://img.shields.io/badge/python-3.10-green
)](https://img.shields.io/badge/python-3.10-green)
[![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/rspy/rspy/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)

[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Security: bandit](https://img.shields.io/badge/security-bandit-green.svg)](https://github.com/PyCQA/bandit)
[![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/rspy/rspy/blob/master/.pre-commit-config.yaml)
Expand All @@ -14,9 +15,13 @@
![Coverage Report](assets/images/coverage.svg)

The core implementation of Fast Rolling Shutter Correction in the Wild, TPAMI 2023 and Towards Nonlinear-Motion-Aware and Occlusion-Robust Rolling Shutter Correction, ICCV 2023.

</div>

| **3GS** | **Gpark** |
| :---------------------------: | :-------------------------------: |
| ![3gs](assets/images/3gs.gif) | ![gpark](assets/images/gpark.gif) |


## Very first steps

### Initialize your code
Expand Down Expand Up @@ -88,71 +93,17 @@ Building a new version of the application contains steps:
- Create a `GitHub release`.
- And... publish 🙂 `poetry publish --build`

## 🎯 What's next

Well, that's up to you 💪🏻. I can only recommend the packages and articles that helped me.

- [`Typer`](https://github.com/tiangolo/typer) is great for creating CLI applications.
- [`Rich`](https://github.com/willmcgugan/rich) makes it easy to add beautiful formatting in the terminal.
- [`Pydantic`](https://github.com/samuelcolvin/pydantic/) – data validation and settings management using Python type hinting.
- [`Loguru`](https://github.com/Delgan/loguru) makes logging (stupidly) simple.
- [`tqdm`](https://github.com/tqdm/tqdm) – fast, extensible progress bar for Python and CLI.
- [`IceCream`](https://github.com/gruns/icecream) is a little library for sweet and creamy debugging.
- [`orjson`](https://github.com/ijl/orjson) – ultra fast JSON parsing library.
- [`Returns`](https://github.com/dry-python/returns) makes you function's output meaningful, typed, and safe!
- [`Hydra`](https://github.com/facebookresearch/hydra) is a framework for elegantly configuring complex applications.
- [`FastAPI`](https://github.com/tiangolo/fastapi) is a type-driven asynchronous web framework.

Articles:

- [Open Source Guides](https://opensource.guide/).
- [A handy guide to financial support for open source](https://github.com/nayafia/lemonade-stand)
- [GitHub Actions Documentation](https://help.github.com/en/actions).
- Maybe you would like to add [gitmoji](https://gitmoji.carloscuesta.me/) to commit names. This is really funny. 😄

## 🚀 Features

### Development features

- Supports for `Python 3.8` and higher.
- [`Poetry`](https://python-poetry.org/) as the dependencies manager. See configuration in [`pyproject.toml`](https://github.com/rspy/rspy/blob/master/pyproject.toml) and [`setup.cfg`](https://github.com/rspy/rspy/blob/master/setup.cfg).
- Automatic codestyle with [`black`](https://github.com/psf/black), [`isort`](https://github.com/timothycrosley/isort) and [`pyupgrade`](https://github.com/asottile/pyupgrade).
- Ready-to-use [`pre-commit`](https://pre-commit.com/) hooks with code-formatting.
- Type checks with [`mypy`](https://mypy.readthedocs.io); docstring checks with [`darglint`](https://github.com/terrencepreilly/darglint); security checks with [`safety`](https://github.com/pyupio/safety) and [`bandit`](https://github.com/PyCQA/bandit)
- Testing with [`pytest`](https://docs.pytest.org/en/latest/).
- Ready-to-use [`.editorconfig`](https://github.com/rspy/rspy/blob/master/.editorconfig), [`.dockerignore`](https://github.com/rspy/rspy/blob/master/.dockerignore), and [`.gitignore`](https://github.com/rspy/rspy/blob/master/.gitignore). You don't have to worry about those things.

### Deployment features

- `GitHub` integration: issue and pr templates.
- `Github Actions` with predefined [build workflow](https://github.com/rspy/rspy/blob/master/.github/workflows/build.yml) as the default CI/CD.
- Everything is already set up for security checks, codestyle checks, code formatting, testing, linting, docker builds, etc with [`Makefile`](https://github.com/rspy/rspy/blob/master/Makefile#L89). More details in [makefile-usage](#makefile-usage).
- [Dockerfile](https://github.com/rspy/rspy/blob/master/docker/Dockerfile) for your package.
- Always up-to-date dependencies with [`@dependabot`](https://dependabot.com/). You will only [enable it](https://docs.github.com/en/github/administering-a-repository/enabling-and-disabling-version-updates#enabling-github-dependabot-version-updates).
- Automatic drafts of new releases with [`Release Drafter`](https://github.com/marketplace/actions/release-drafter). You may see the list of labels in [`release-drafter.yml`](https://github.com/rspy/rspy/blob/master/.github/release-drafter.yml). Works perfectly with [Semantic Versions](https://semver.org/) specification.

### Open source community features

- Ready-to-use [Pull Requests templates](https://github.com/rspy/rspy/blob/master/.github/PULL_REQUEST_TEMPLATE.md) and several [Issue templates](https://github.com/rspy/rspy/tree/master/.github/ISSUE_TEMPLATE).
- Files such as: `LICENSE`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, and `SECURITY.md` are generated automatically.
- [`Stale bot`](https://github.com/apps/stale) that closes abandoned issues after a period of inactivity. (You will only [need to setup free plan](https://github.com/marketplace/stale)). Configuration is [here](https://github.com/rspy/rspy/blob/master/.github/.stale.yml).
- [Semantic Versions](https://semver.org/) specification with [`Release Drafter`](https://github.com/marketplace/actions/release-drafter).
- A light weight library for rolling shutter correction which is easy to use.
- Support linear, qudratic and cubic motion model.

## Installation

```bash
pip install -U rspy
```

or install with `Poetry`

```bash
poetry add rspy
```



### Makefile usage
### Usage

[`Makefile`](https://github.com/rspy/rspy/blob/master/Makefile) contains a lot of functions for faster development.

Expand Down Expand Up @@ -357,8 +308,8 @@ We use [`Release Drafter`](https://github.com/marketplace/actions/release-drafte

### List of labels and corresponding titles

| **Label** | **Title in Releases** |
| :-----------------------------------: | :---------------------: |
| **Label** | **Title in Releases** |
| :-----------------------------------: | :--------------------: |
| `enhancement`, `feature` | 🚀 Features |
| `bug`, `refactoring`, `bugfix`, `fix` | 🔧 Fixes & Refactoring |
| `build`, `ci`, `testing` | 📦 Build System & CI/CD |
Expand Down
Binary file added assets/images/3gs.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions assets/images/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/gpark.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion rspy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# type: ignore[attr-defined]
"""The core implementation of Fast Rolling Shutter Correction in the Wild, TPAMI 2023 and Towards Nonlinear-Motion-Aware and Occlusion-Robust Rolling Shutter Correction, ICCV 2023."""

import sys
from importlib import metadata as importlib_metadata

from .solver import cubic_flow, linear_flow, quadratic_flow
from .utils import feats_sampling


def get_version() -> str:
try:
Expand All @@ -13,3 +15,5 @@ def get_version() -> str:


version: str = get_version()

__all__ = ["linear_flow", "quadratic_flow", "cubic_flow", "feats_sampling", "version"]
52 changes: 52 additions & 0 deletions rspy/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import argparse
from pathlib import Path

import torch
from mmflow.apis import inference_model, init_model
from mmflow.datasets import visualize_flow
from PIL import Image
from torchvision import transforms
from torchvision.utils import save_image

from rspy.solver import cubic_flow, linear_flow, quadratic_flow
from rspy.utils import feats_sampling

parser = argparse.ArgumentParser()
parser.add_argument("--input", type=str, help="input file or directory")
parser.add_argument("--output", type=str, help="output directory")
parser.add_argument("--model", type=str, default="linear", help="linear | quadratic | cubic")
parser.add_argument("--gamma", type=float, default=0.9, help="the readout reatio")
parser.add_argument("--tau", type=int, default=0, help="the timestamp warping to")
parser.add_argument("--fconfig", type=str, default="raft_8x2_100k_mixed_368x768", help="mmflow config file")
parser.add_argument("--device", type=str, default="cuda:0", help="cpu | cuda:0")
args = parser.parse_args()


def main():
assert args.model in ["linear", "quadratic", "cubic"]
input, output = Path(args.input), Path(args.output)
image_paths = sorted(list(input.iterdir()))

# init a optical-flow model
config_file, checkpoint_file = f"{args.fconfig}.py", f"{args.fconfig}.pth"
model = init_model(config_file, checkpoint_file, device=args.device)

# * inference flow
num = {"linear": 2, "quadratic": 3, "cubic": 4}[args.model]
flows = inference_model(model, image_paths[: num - 1], image_paths[1:num]) # list of numpy.ndarray
torch_flows = [torch.from_numpy(flow).unsqueeze(0).to(args.device) for flow in flows] # * list (1,h,w,2)
for i, flow in enumerate(flows):
visualize_flow(flow, output / f"{i:04d}.png")

# * rolling shutter correctin
solver = eval(f"{args.model}_flow")
F0tau = solver(*torch_flows[:num], args.gamma, args.tau) # * (1,h,w,2)

# * warp image
rs_path = image_paths[num // 2]
tsfm = transforms.Compose([transforms.ToTensor()])
rs_image = tsfm(Image.open(rs_path).convert("RGB")).unsqueeze(0).to(args.device) # * (1,3,h,w)
rsc_image = feats_sampling(rs_image, -F0tau)

# * save image
save_image(rsc_image, output / f"rsc_{rs_path.stem}.png")
Binary file added rspy/demo/rs_00093.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added rspy/demo/rs_00094.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added rspy/demo/rs_00095.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added rspy/demo/rs_00096.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 0 additions & 19 deletions rspy/example.py

This file was deleted.

119 changes: 119 additions & 0 deletions rspy/solver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import torch
from einops import rearrange


def linear_flow(F01: torch.Tensor, gamma: float, tau: float) -> torch.Tensor:
"""solve the linear motion matrix and predict the correction feild.
Args:
F01: torch.Tensor (b,h,w,2), flow 0 -> 1
gamma: float, the readout reatio
tau: float, the timestamp warping to
Returns:
torch.Tensor: the correction feild to tau.
"""
h, w = F01.shape[1:3]
t01 = 1 + gamma / h * F01[:, :, :, 1] # * (b, h, w)

# solve the linear motion matrix
M = F01 / rearrange(t01, "b h w -> b h w 1") # * (b, h, w, 2)

# predict the correction feild
grid_y, _ = torch.meshgrid(
torch.arange(0, h, device=F01.device, requires_grad=False),
torch.arange(0, w, device=F01.device, requires_grad=False),
) # * (h, w)

t0tau = tau - gamma / h * grid_y # * (h, w)
F0tau = rearrange(t0tau, "h w -> h w 1") * M # * (b, h, w, 2)

return F0tau


def quadratic_flow(F0n1: torch.Tensor, F01: torch.Tensor, gamma: float, tau: float) -> torch.Tensor:
"""solve the quadratic motion matrix and predict the correction feild.
Args:
F0n1: torch.Tensor (b,h,w,2), flow 0 -> -1
F01: torch.Tensor (b,h,w,2), flow 0 -> 1
gamma (float): the readout reatio
tau (float): the timestamp warping to
Returns:
torch.Tensor: the correction feild to tau.
"""
h, w = F0n1.shape[1:3]
t0n1 = -1 + gamma / h * F0n1[:, :, :, 1] # * (b, h, w)
t01 = 1 + gamma / h * F01[:, :, :, 1] # * (b, h, w)

# solve the quadratic motion matrix
A = rearrange(
torch.stack([t0n1, 0.5 * t0n1 ** 2, t01, 0.5 * t01 ** 2], dim=-1),
"b h w (m n) -> b h w m n",
m=2,
n=2,
) # * (b, h, w, 2, 2)

B = torch.stack([F0n1, F01], dim=-2) # * (b, h, w, 2, 2)
M = torch.linalg.solve(A, B) # * (b, h, w, 2, 2)

# predict the correction feild
grid_y, _ = torch.meshgrid(
torch.arange(0, h, device=F0n1.device, requires_grad=False),
torch.arange(0, w, device=F0n1.device, requires_grad=False),
)
t0tau = tau - gamma / h * grid_y # * (h, w)

Atau = rearrange(torch.stack([t0tau, 0.5 * t0tau ** 2], dim=-1), "h w m -> h w 1 m") # * (h, w, 1, 2)
F0tau = rearrange(Atau @ M, "b h w 1 n -> b h w n") # * (b, h, w, 2)

return F0tau


def cubic_flow(F0n2: torch.Tensor, F0n1: torch.Tensor, F01: torch.Tensor, gamma: float, tau: float) -> torch.Tensor:
"""solve the cubic motion matrix and predict the correction feild.
Args:
F0n1: torch.Tensor (b,h,w,2): flow 0 -> -1
F01: torch.Tensor (b,h,w,2): flow 0 -> 1
F02: torch.Tensor (b,h,w,2): flow 0 -> 2
gamma: (float): the readout reatio
tau: (float): the timestamp warping to
Returns:
torch.Tensor: the correction feild to tau.
"""
h, w = F0n1.shape[1:3]
t0n2 = -2 + gamma / h * F0n2[:, :, :, 1]
t0n1 = -1 + gamma / h * F0n1[:, :, :, 1]
t01 = 1 + gamma / h * F01[:, :, :, 1]

# solve the quadratic motion matrix
A = rearrange(
torch.stack(
[
t0n2,
0.5 * t0n2 ** 2,
1 / 6 * t0n2 ** 3,
t0n1,
0.5 * t0n1 ** 2,
1 / 6 * t0n1 ** 3,
t01,
0.5 * t01 ** 2,
1 / 6 * t01 ** 3,
],
dim=-1,
),
"b h w (m n) -> b h w m n",
m=3,
n=3,
) # * (b, h, w, 3, 3)
B = torch.stack([F0n2, F0n1, F01], dim=-2) # * (b, h, w, 3, 2)
M = torch.linalg.solve(A, B) # * (b, h, w, 3, 2)

# predict the correction feild
grid_y, _ = torch.meshgrid(
torch.arange(0, h, device=F0n1.device, requires_grad=False),
torch.arange(0, w, device=F0n1.device, requires_grad=False),
)
t0tau = tau - gamma / h * grid_y # * (h, w)

Atau = rearrange(torch.stack([t0tau, 0.5 * t0tau ** 2, 1 / 6 * t0tau ** 3], dim=-1), "h w m -> h w 1 m")
F0tau = rearrange(Atau @ M, "b h w 1 n -> b h w n") # * (b, h, w, 2)

return F0tau
41 changes: 41 additions & 0 deletions rspy/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import numpy as np
import torch
import torch.nn.functional as F


def feats_sampling(
x,
flow,
interpolation="bilinear",
padding_mode="zeros",
align_corners=True,
):
"""return warped images with flows in shape(B, C, H, W)
Args:
x: shape(B, C, H, W)
flow: shape(B, H, W, 2)
"""
if x.size()[-2:] != flow.size()[1:3]:
raise ValueError(
f"The spatial sizes of input ({x.size()[-2:]}) and " f"flow ({flow.size()[1:3]}) are not the same."
)
h, w = x.shape[-2:]

# create mesh grid
grid_y, grid_x = torch.meshgrid(torch.arange(0, h), torch.arange(0, w))
grid = torch.stack((grid_x, grid_y), 2).type_as(x) #! (h, w, 2)
grid.requires_grad = False
grid_flow = grid + flow

# scale grid_flow to [-1,1]
grid_flow_x = 2.0 * grid_flow[:, :, :, 0] / max(w - 1, 1) - 1.0
grid_flow_y = 2.0 * grid_flow[:, :, :, 1] / max(h - 1, 1) - 1.0
grid_flow = torch.stack((grid_flow_x, grid_flow_y), dim=3)
output = F.grid_sample(
x,
grid_flow,
mode=interpolation,
padding_mode=padding_mode,
align_corners=align_corners,
)
return output
Binary file added scripts/GPark_QRST_seq_cropped.mp4
Binary file not shown.
Binary file added scripts/GPark_rs_seq_cropped.mp4
Binary file not shown.
Loading

0 comments on commit b07530b

Please sign in to comment.