Skip to content

feat: Create image denoising web application #292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
95 changes: 67 additions & 28 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
t Byte-compiled / optimized / DLL files
deps.txt
archive
saver
*~
styles
pngs
preds

*.sw*
data
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Virtual environment
venv/
ENV/
env/
.env
.venv

# IDE / Editor specific
.vscode/
.idea/
*.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
*.sublime-project
*.swp
*.swo
.spyderproject
.ropeproject

# Temporary files
*.tmp
*~
.DS_Store
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
Expand All @@ -30,13 +50,15 @@ lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
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

Expand All @@ -47,21 +69,26 @@ pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
# *.log (covered by *.log below)
# local_settings.py (can be project specific)
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
Expand All @@ -76,24 +103,36 @@ docs/_build/
# PyBuilder
target/

# IPython Notebook
# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# celery beat schedule file
# PEP 582; __pypackages__ directory
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# dotenv
.env
# SageMath files
.sage/

# virtualenv
venv/
ENV/
# General logs
*.log

# Spyder project settings
.spyderproject
# Application-specific
# Ignore uploaded and processed images, but keep the directories via .gitkeep
denoise_app/uploads/*
!denoise_app/uploads/.gitkeep
denoise_app/denoised/*
!denoise_app/denoised/.gitkeep

# Rope project settings
.ropeproject
# Test artifacts from this project
tests/test_temp_uploads/
tests/test_temp_denoised/
65 changes: 65 additions & 0 deletions denoise_app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import os
from flask import Flask, request, render_template, redirect, url_for, send_from_directory
from werkzeug.utils import secure_filename
from denoising import denoise_image # Assuming denoising.py is in the same directory

app = Flask(__name__)

# Configure upload and denoised folders
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
UPLOAD_FOLDER = os.path.join(APP_ROOT, 'uploads')
DENOISED_FOLDER = os.path.join(APP_ROOT, 'denoised')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['DENOISED_FOLDER'] = DENOISED_FOLDER

# Ensure directories exist
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(DENOISED_FOLDER, exist_ok=True)

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

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

@app.route('/upload', methods=['POST'])
def upload_file():
if 'image' not in request.files:
return redirect(request.url) # Should be redirect('/') or url_for('index')

file = request.files['image']
if file.filename == '':
return redirect(request.url) # Should be redirect('/') or url_for('index')

if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
uploaded_image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(uploaded_image_path)

denoised_image_output_path = denoise_image(uploaded_image_path, app.config['DENOISED_FOLDER'])

if denoised_image_output_path:
denoised_filename = os.path.basename(denoised_image_output_path)
original_image_url = url_for('serve_uploaded_file', filename=filename)
denoised_image_url = url_for('serve_denoised_file', filename=denoised_filename)
return render_template('result.html', original_image_url=original_image_url, denoised_image_url=denoised_image_url)
else:
# Handle denoising failure, e.g., redirect to index or show an error
return redirect(url_for('index')) # Or a specific error page

return redirect(url_for('index')) # If file type not allowed or other issues

@app.route('/uploads/<filename>')
def serve_uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

@app.route('/denoised/<filename>')
def serve_denoised_file(filename):
return send_from_directory(app.config['DENOISED_FOLDER'], filename)

if __name__ == '__main__':
app.run(debug=True)
1 change: 1 addition & 0 deletions denoise_app/denoised/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file is to ensure the directory is tracked by git.
73 changes: 73 additions & 0 deletions denoise_app/denoising.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import cv2
import os
import numpy as np

def denoise_image(input_image_path, output_directory):
"""
Reads an image, applies a denoising algorithm, and saves the denoised image.

Args:
input_image_path (str): The path to the input image.
output_directory (str): The directory to save the denoised image.

Returns:
str: The full path to the saved denoised image, or None if an error occurs.
"""
if not os.path.exists(input_image_path):
print(f"Error: Input image path does not exist: {input_image_path}")
return None

img = cv2.imread(input_image_path)
if img is None:
print(f"Error: Could not read image from path: {input_image_path}")
return None

try:
denoised_img = cv2.fastNlMeansDenoisingColored(img, None, h=10, hColor=10, templateWindowSize=7, searchWindowSize=21)

if not os.path.exists(output_directory):
os.makedirs(output_directory)

base_filename = os.path.basename(input_image_path)
denoised_filename = f"denoised_{base_filename}"
output_image_path = os.path.join(output_directory, denoised_filename)

cv2.imwrite(output_image_path, denoised_img)
return output_image_path

except Exception as e:
print(f"Error during denoising or saving image: {e}")
return None

if __name__ == '__main__':
# Create dummy files for testing
# This part is for basic testing and might need adjustment based on project structure
if not os.path.exists("test_images"):
os.makedirs("test_images")
if not os.path.exists("test_output"):
os.makedirs("test_output")

# Create a dummy image (e.g., a black square)
dummy_image = np.zeros((100, 100, 3), dtype=np.uint8)
dummy_input_path = "test_images/dummy_input.png"
cv2.imwrite(dummy_input_path, dummy_image)

print(f"Attempting to denoise: {dummy_input_path}")
denoised_path = denoise_image(dummy_input_path, "test_output")

if denoised_path:
print(f"Denoised image saved to: {denoised_path}")
# Clean up dummy files
os.remove(dummy_input_path)
os.remove(denoised_path)
os.rmdir("test_images")
os.rmdir("test_output")
else:
print("Denoising failed.")
# Clean up dummy input if it exists
if os.path.exists(dummy_input_path):
os.remove(dummy_input_path)
if os.path.exists("test_images"):
os.rmdir("test_images")
if os.path.exists("test_output"):
os.rmdir("test_output")
1 change: 1 addition & 0 deletions denoise_app/static/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file is to ensure the directory is tracked by git.
Loading