Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# Ruff stuff:
.ruff_cache/

# PyPI configuration file
.pypirc


# custom files
Checkpoints/
Images/
__pycache__/
dataset/
libs/__pycache__/utils.cpython-310.pyc
libs/__pycache__/vgg16.cpython-310.pyc
static/output.png
.DS_Store
comparison.png
69 changes: 54 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@

## Introduction

Animation movie companies like Pixar and Dreamworks render their 3d scenes using a technique called Pathtracing which enables them to create high quality photorealistic frames. Pathtracing involves shooting 1000s of rays into a pixel randomly(Monte Carlo) which will then hit the objects in the scene and based on the reflective property of the object the rays reflect or refract or get absorbed. The colors returned by these rays are averaged to get the color of the pixel and this process is repeated for all the pixels. Due to the computational complexity it might take 8-16 hours to render a single frame.
Animation movie companies like Pixar and Dreamworks render their 3d scenes using a technique called Pathtracing which enables them to create high quality photorealistic frames. Pathtracing involves shooting 1000's of rays into a pixel randomly(Monte Carlo) which will then hit the objects in the scene and based on the reflective property of the object the rays reflect or refract or get absorbed. The colors returned by these rays are averaged to get the color of the pixel and this process is repeated for all the pixels. Due to the computational complexity it might take 8-16 hours to render a single frame.

We are proposing a neural network based solution for reducing 8-16 hours to a couple of seconds using a Generative Adversarial Network. The main idea behind this proposed method is to render using small number of samples per pixel (let say 4 spp or 8 spp instead of 32K spp) and pass the noisy image to our network, which will generate a photorealistic image with high quality.

# Demo Video [Link](https://www.youtube.com/watch?v=Yh_Bsoe-Qj4)

[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/Yh_Bsoe-Qj4/0.jpg)](https://www.youtube.com/watch?v=Yh_Bsoe-Qj4)



#### Table of Contents

* [Installation](#installation)
Expand All @@ -25,24 +23,65 @@ We are proposing a neural network based solution for reducing 8-16 hours to a co

## Installation

To run the project you will need:
* python 3.5
* tensorflow (v1.1 or v1.0)
* PIL
* [CKPT FILE](https://uofi.box.com/shared/static/21a5jwdiqpnx24c50cyolwzwycnr3fwe.gz)
* [Dataset](https://uofi.box.com/shared/static/gy0t3vgwtlk1933xbtz1zvhlakkdac3n.zip)
### Prerequisites
* Python 3.5 or higher
* pip (Python package installer)
* virtualenv (recommended)

### Setup Steps

1. Clone the repository:
```bash
git clone https://github.com/yourusername/ImageDenoisingGAN.git
cd ImageDenoisingGAN
```

2. Create and activate a virtual environment:
```bash
# Create virtual environment
python -m venv venv

# Activate virtual environment
# On Windows:
venv\Scripts\activate
# On macOS/Linux:
source venv/bin/activate
```

3. Install dependencies:
```bash
pip install -r requirements.txt
```

4. Download required files:
* [CKPT FILE](https://uofi.box.com/shared/static/21a5jwdiqpnx24c50cyolwzwycnr3fwe.gz)
* [Dataset](https://uofi.box.com/shared/static/gy0t3vgwtlk1933xbtz1zvhlakkdac3n.zip) (only if you want to train)

### Required Files Structure
```
ImageDenoisingGAN/
├── venv/ # Virtual environment (created during setup)
├── Checkpoints/ # Extracted CKPT files go here
├── dataset/ # Dataset folder (if training)
├── static/ # Output images
└── requirements.txt # Project dependencies
```

## Running

Once you have all the depenedencies ready, do the folowing:

Download the dataset extract it to a folder named 'dataset' (ONLY if you want to train, not needed to run).
Once you have all the dependencies ready, do the following:

Extract the CKPT files to a folder named 'Checkpoints'
1. Extract the CKPT files to a folder named 'Checkpoints'

Run main.py -- python3 main.py
2. Run the application:
```bash
# Make sure your virtual environment is activated
python main.py
```

Go to the browser, if you are running it on a server then [ip-address]:8888, if you are on your local machine then localhost:8888
3. Access the application:
* If running locally: http://localhost:80
* If running on a server: http://[server-ip]:80

## Dataset
We picked random 40 images from pixar movies, added gaussian noise of different standard deviation, 5 sets of 5 different standard deviation making a total of 1000 images for the training set. For validation we used 10 images completely different from the training set and added gaussian noise. For testing we had both added gaussian images and real noisy images.
Expand Down
63 changes: 30 additions & 33 deletions conv_helper.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
import tensorflow as tf
import tensorflow.contrib.slim as slim

from utils import *

def conv_layer(input_image, ksize, in_channels, out_channels, stride, scope_name, activation_function=lrelu, reuse=False):
with tf.variable_scope(scope_name):
filter = tf.Variable(tf.random_normal([ksize, ksize, in_channels, out_channels], stddev=0.03))
output = tf.nn.conv2d(input_image, filter, strides=[1, stride, stride, 1], padding='SAME')
output = slim.batch_norm(output)
if activation_function:
output = activation_function(output)
return output, filter

def residual_layer(input_image, ksize, in_channels, out_channels, stride, scope_name):
with tf.variable_scope(scope_name):
output, filter = conv_layer(input_image, ksize, in_channels, out_channels, stride, scope_name+"_conv1")
output, filter = conv_layer(output, ksize, out_channels, out_channels, stride, scope_name+"_conv2")
output = tf.add(output, tf.identity(input_image))
return output, filter

def transpose_deconvolution_layer(input_tensor, used_weights, new_shape, stride, scope_name):
with tf.varaible_scope(scope_name):
output = tf.nn.conv2d_transpose(input_tensor, used_weights, output_shape=new_shape, strides=[1, stride, stride, 1], padding='SAME')
output = tf.nn.relu(output)
return output
class ResidualBlock(tf.keras.layers.Layer):
def __init__(self, out_channels, ksize, stride, name=None):
super(ResidualBlock, self).__init__(name=name)
self.conv1 = tf.keras.layers.Conv2D(out_channels, ksize, strides=stride, padding='same')
self.conv2 = tf.keras.layers.Conv2D(out_channels, ksize, strides=stride, padding='same')
self.bn1 = tf.keras.layers.BatchNormalization()
self.bn2 = tf.keras.layers.BatchNormalization()
self.activation = tf.keras.layers.LeakyReLU(0.2)
self.add = tf.keras.layers.Add()

def resize_deconvolution_layer(input_tensor, new_shape, scope_name):
with tf.variable_scope(scope_name):
output = tf.image.resize_images(input_tensor, (new_shape[1], new_shape[2]), method=1)
output, unused_weights = conv_layer(output, 3, new_shape[3]*2, new_shape[3], 1, scope_name+"_deconv")
return output
def call(self, inputs):
x = self.conv1(inputs)
x = self.bn1(x)
x = self.activation(x)

x = self.conv2(x)
x = self.bn2(x)
x = self.activation(x)

return self.add([x, inputs])

def deconvolution_layer(input_tensor, new_shape, scope_name):
return resize_deconvolution_layer(input_tensor, new_shape, scope_name)
class DeconvolutionBlock(tf.keras.layers.Layer):
def __init__(self, out_channels, name=None):
super(DeconvolutionBlock, self).__init__(name=name)
self.conv = tf.keras.layers.Conv2D(out_channels, 3, padding='same')
self.bn = tf.keras.layers.BatchNormalization()
self.activation = tf.keras.layers.LeakyReLU(0.2)

def output_between_zero_and_one(output):
output +=1
return output/2
def call(self, inputs, target_height, target_width):
x = tf.image.resize(inputs, (target_height, target_width), method='bilinear')
x = self.conv(x)
x = self.bn(x)
return self.activation(x)
19 changes: 12 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
from flask import Flask, render_template, request, jsonify, send_file
import numpy as np
import scipy.misc
import base64
from io import BytesIO
from test import *
import time
import cv2

app = Flask(__name__)

@app.route('/')
def index():
return render_template("index.html")


@app.route('/denoisify', methods=['GET', 'POST'])
def denoisify():
if request.method == "POST":
inputImg = request.files['file']
outputImg = denoise(inputImg)
scipy.misc.imsave('static/output.png', outputImg)
return jsonify(result="Success")
# Convert the file object to a numpy array using OpenCV
input_array = cv2.imdecode(np.frombuffer(inputImg.read(), np.uint8), cv2.IMREAD_COLOR)
input_array = cv2.cvtColor(input_array, cv2.COLOR_BGR2RGB) # Convert BGR to RGB
outputImg = denoise(input_array)
print(f"Out image: {outputImg}")

# Save the output image using OpenCV
output_image = (outputImg * 255).astype(np.uint8) # Scale to 0-255 range
output_image = cv2.cvtColor(output_image, cv2.COLOR_RGB2BGR) # Convert RGB to BGR for saving
cv2.imwrite('static/output.png', output_image)

return jsonify(result="Success")

if __name__=="__main__":
app.run(host="0.0.0.0",port="80")
Loading