Skip to content
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

HDR image upload and moderation work + possible setup fixes #130

Merged
merged 18 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9182389
Initial work to upload hdr images to a local upload folder.
CheesyLaZanya Nov 29, 2024
d8de0a2
For HDR upload, add support for multiple files, proper UX with indica…
CheesyLaZanya Nov 29, 2024
2c84c29
Update frontend upload process to call APIs to get file information, …
CheesyLaZanya Nov 30, 2024
1ed4b65
Add user ID to uploaded file names for uniqueness, better catch error…
CheesyLaZanya Dec 1, 2024
e5b0d53
Capture hdr upload user ID in metadata file as well, and refactor to …
CheesyLaZanya Dec 2, 2024
298035d
Add the new docker compose variables to the .env.template file.
CheesyLaZanya Dec 2, 2024
6aff799
Update metadata retrieval endpoint to use exiftool as well.
CheesyLaZanya Dec 4, 2024
d295151
Add initial image moderation interface.
CheesyLaZanya Dec 7, 2024
4161124
Add initial clicking preview to enlarge image.
CheesyLaZanya Dec 8, 2024
4c7a322
Move moderation page styles to a dedicated css file.
CheesyLaZanya Dec 9, 2024
6429c0e
Update moderation page to show user email instead of ID number.
CheesyLaZanya Dec 14, 2024
735c13a
Expand on S3 preparation/setup work for HDR image upload and moderati…
CheesyLaZanya Dec 14, 2024
a896c49
Remove Axios in favour of Node fetch
CheesyLaZanya Dec 21, 2024
21a9265
Fix hdr moderation page to allow for scrolling and visible pagination.
CheesyLaZanya Dec 21, 2024
b67e852
Fix style of previous and next buttons for image moderation UI.
CheesyLaZanya Dec 21, 2024
658bcf3
Fix issues of outdated/conflicting pydantic versions, and fix issues …
CheesyLaZanya Dec 28, 2024
5160c27
Comment out the odr_database taskfile inclusion and update order of i…
CheesyLaZanya Dec 29, 2024
5b17787
Remove the odr_database module.
CheesyLaZanya Dec 29, 2024
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
4 changes: 4 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ OAUTH2_REDIRECT_PATH=docs
## Hugging Face
HF_TOKEN=your_access_token
HF_HDR_DATASET_NAME=openmodelinitiative/hdr-submissions

## Docker compose variables
NODE_ENV=development
UPLOAD_DIR=./uploads
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,6 @@ models_cache/

# Embedding models
**/models--**

# Local uploads
uploads/
26 changes: 18 additions & 8 deletions GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,19 @@ To setup your environment run `task setup`

Along with installing the necessary components, this will copy the `.env.template` setup into a `.env` file.

You can update this file if you'd like to change any of the default values for your development environment.
You can update this file if you'd like to change any of the default values for your development environment. You will at minimum need to provide a valid ID and SECRET for at least one authentication method.

You will also need to manually copy the `.env.template` file in `modules/odr_frontend` to a `.env` file in the same folder, and make matching updates.

# Running The Local Environment

To start your virtual environment, run `task activate-venv`
To start your virtual environment, run `task activate-venv` or `source ./venv/bin/activate`

Then to start all the required services, run `task start-all`
Then to start all the required services, run `task dev`

This will spin up the required front end, api, and database components.
This will spin up the required front end, api, and database containers.

Before the site will 'work' you will need to run `task data:migrate` to create the required data and tables

By default, you will have the following:

Expand All @@ -74,14 +77,23 @@ By default, you will have the following:
- Interactive OpenAPI documentation at: http://localhost:31100/docs
- Redoc documentation at: http://localhost:31100/redoc
- A PGAdmin interface at: http://localhost:35050
To connect to the database, open http://localhost:35050/browser/

Sign in with your PGADMIN_DEFAULT_EMAIL and PGADMIN_DEFAULT_PASSWORD from your .env file

On the left, right click servers, and click Register > Server

Name the DB something like omi-database, then switch to the connection tab

The host name/address will be 'postgres', the username will be your .env POSTGRES_USER, and the password will be your .env POSTGRES_PASSWORD

# Running Tests

Before committing any changes, ensure you run all tests to be sure existing behaviour continues to work. If any tests require updates to pass with your changes, ensure you update them as well.

To run all tests, run `task test-all`
To run all tests, run `task test-all`. Note this is not working at the moment and needs some work but some tests are run via the pipeline automatically.

(Todo: Add instructions for how to run the diffferent types individual as well to check work under development.)
(Todo: Add instructions for how to run the different types individual as well to check work under development.)

# Committing Changes

Expand All @@ -93,5 +105,3 @@ Before committing any changes, remember DCO and sign offs! If any commits are no
To stop the environment, start by using ctrl+c (or cmd+c) to stop the current processes.

Then use `task stop-all` to full stop the environment.

If you want to remove the docker volumes used by the database, run `task db:teardown`
7 changes: 2 additions & 5 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ version: '3'

includes:
api: ./modules/odr_api/Taskfile.api.yml
db: ./modules/odr_database/Taskfile.yml
core: ./modules/odr_core/Taskfile.yml
data: ./modules/odr_datamodel/Taskfile.yml
frontend: ./modules/odr_frontend/Taskfile.yml
Expand Down Expand Up @@ -34,7 +33,7 @@ tasks:
platforms: [linux, darwin]
- cmd: docker network inspect omi-network >/dev/null 2>&1 || docker network create omi-network
- cmd: task setup-venv
- cmd: task frontend:install
- cmd: task frontend:install-dev

setup-venv:
desc: Setup the virtual environment
Expand Down Expand Up @@ -74,7 +73,6 @@ tasks:
- |
task core:test &
task core:test-db &
task db:test-postgres &
task api-test:test-all &
task monitoring:test &
wait
Expand All @@ -87,10 +85,9 @@ tasks:
- docker compose logs -f

smoke_tests:
desc: Run smoke tests for both API and PostgreSQL
desc: Run smoke tests for the API
cmds:
- task api-test:health-check
- task db:test-postgres

build-all:
desc: Build all services
Expand Down
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@ services:
- ./modules/odr_frontend/docker/entrypoint.sh:/app/docker/entrypoint.sh
- odr_frontend_node_modules:/app/node_modules
- odr_frontend_pnpm_store:/app/.pnpm-store
- ${UPLOAD_DIR:-./uploads}:/app/uploads
ports:
- "5173:5173"
environment:
NODE_ENV: development
NODE_ENV: ${NODE_ENV:-development}
API_SERVICE_URL: http://odr-api:31100/api/v1
UPLOAD_DIR: /app/uploads
env_file:
- ./modules/odr_frontend/.env
networks:
Expand Down
81 changes: 40 additions & 41 deletions modules/odr_api/odr_api/api/endpoints/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from typing import Any
import base64
import traceback
import json
import subprocess
import tempfile
import os
Expand Down Expand Up @@ -72,21 +73,6 @@ def calculate_entropy(tensor: torch.Tensor):


# Helper functions for HDR metadata and preview conversion
def extract_metadata(image_bytes: bytes) -> Dict:
metadata = {}
try:
with Image.open(BytesIO(image_bytes)) as img:
exif_data = img.getexif()
if exif_data:
for tag_id, value in exif_data.items():
tag = TAGS.get(tag_id, tag_id)
metadata[tag] = value
print('Metadata extracted from image file')
except UnidentifiedImageError:
print('Could not extract metadata from image')
return metadata


def convert_ifd_rational(value):
if isinstance(value, IFDRational):
return float(value)
Expand All @@ -95,16 +81,40 @@ def convert_ifd_rational(value):
return value


def get_desired_metadata(metadata: Dict) -> Dict[str, Any]:
important_keys = ['Make', 'Model', 'BitsPerSample', 'BaselineExposure', 'LinearResponseLimit', 'ImageWidth', 'ImageLength', 'DateTime']
result = {key: convert_ifd_rational(metadata.get(key)) for key in important_keys if key in metadata}
def get_metadata_with_exiftool(image_bytes: bytes) -> Dict[str, Any]:
with tempfile.NamedTemporaryFile(delete=False, suffix='.dng') as temp_input_file:
temp_input_file.write(image_bytes)
temp_input_filename = temp_input_file.name

try:
tags = [
'-Make', '-Model', '-BitsPerSample', '-BaselineExposure',
'-LinearResponseLimit', '-ImageWidth', '-ImageHeight',
'-DNGVersion'
]

result = subprocess.run(
['exiftool', '-j'] + tags + [temp_input_filename],
capture_output=True, text=True, check=True
)

metadata = json.loads(result.stdout)[0]

if 'DNGVersion' in metadata:
dng_version = metadata['DNGVersion']
version_string = '.'.join(str(b) for b in dng_version)
result['DNGVersion'] = version_string
if 'BitsPerSample' in metadata:
metadata['BitsPerSample'] = int(metadata['BitsPerSample'])
if 'BaselineExposure' in metadata:
metadata['BaselineExposure'] = float(metadata['BaselineExposure'])
if 'LinearResponseLimit' in metadata:
metadata['LinearResponseLimit'] = float(metadata['LinearResponseLimit'])
if 'ImageWidth' in metadata:
metadata['ImageWidth'] = int(metadata['ImageWidth'])
if 'ImageHeight' in metadata:
metadata['ImageHeight'] = int(metadata['ImageHeight'])

return result
return metadata

finally:
os.remove(temp_input_filename)


def convert_dng_to_jpg(dng_bytes: bytes) -> bytes:
Expand Down Expand Up @@ -162,27 +172,19 @@ async def create_jpg_preview(file: UploadFile = File(...)):
raise HTTPException(status_code=400, detail=str(e))


# Endpoint for metadata retrieval
@router.post("/image/metadata")
async def get_image_metadata(file: UploadFile = File(...)):
try:
contents = await file.read()
metadata = extract_metadata(contents)
metadata = get_metadata_with_exiftool(contents)

important_metadata = get_desired_metadata(metadata)

return important_metadata
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return metadata
except subprocess.CalledProcessError as e:
raise HTTPException(status_code=400, detail=f"Error processing image with exiftool: {str(e)}")
except json.JSONDecodeError as e:
raise HTTPException(status_code=500, detail=f"Error parsing exiftool output: {str(e)}")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))


# For debugging
# docker cp $(docker ps --filter name=omi-postgres-odr-api -q):./app/cleaned_image_exiftool.dng ./cleaned_image_exiftool.dng
# def save_image_locally(image_bytes: bytes, filename: str):
# with open(filename, 'wb') as f:
# f.write(image_bytes)
raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")


def remove_metadata_with_exiftool(input_bytes):
Expand Down Expand Up @@ -252,9 +254,6 @@ async def clean_image_metadata(file: UploadFile = File(...)):
contents = await file.read()

cleaned_image_bytes = remove_metadata_with_exiftool(contents)
# For debugging
# save_image_locally(cleaned_image_bytes, 'cleaned_image_exiftool.dng')

encoded_image = base64.b64encode(cleaned_image_bytes).decode('utf-8')

return {
Expand Down
2 changes: 1 addition & 1 deletion modules/odr_api/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
uvicorn==0.30.5
fastapi==0.115.4
pydantic[email]==2.8.2
pydantic[email]==2.10.4
sqlalchemy==2.0.31
pytest==8.3.2
starlette==0.40.0
Expand Down
2 changes: 1 addition & 1 deletion modules/odr_caption/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ qwen-vl-utils[decord]
fastapi
click
openai
pydantic
pydantic==2.10.4
pillow
python-dotenv
torch
Expand Down
2 changes: 1 addition & 1 deletion modules/odr_core/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
alembic==1.13.2
sqlalchemy==2.0.31
pydantic==2.8.2
pydantic==2.10.4
pydantic-settings==2.4.0
pytest==8.3.2
loguru==0.7.2
Expand Down
64 changes: 0 additions & 64 deletions modules/odr_database/Taskfile.yml

This file was deleted.

Loading
Loading