Skip to content

Commit

Permalink
Merge pull request #130 from Open-Model-Initiative/122-create-hdr-ima…
Browse files Browse the repository at this point in the history
…ge-moderation-ui

Initial HDR image upload and moderation work + setup fixes
  • Loading branch information
CheesyLaZanya authored Jan 5, 2025
2 parents 993f0c5 + 5b17787 commit f392157
Show file tree
Hide file tree
Showing 28 changed files with 2,063 additions and 233 deletions.
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

0 comments on commit f392157

Please sign in to comment.