brestok commited on
Commit
34de76f
·
1 Parent(s): 642914f

Add Git LFS support for binary files

Browse files
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.db filter=lfs diff=lfs merge=lfs -text
2
+ *.jpg filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,128 +1,10 @@
1
- # LUT Transformation API
2
-
3
- A FastAPI-based service for transforming .cube LUT files using AI-generated color adjustments and creating split preview images.
4
-
5
- ## Features
6
-
7
- - **LUT Transformation**: Transform .cube files using JSON-based color adjustments
8
- - **AI Integration**: Generate color adjustments based on natural language prompts
9
- - **Split Preview**: Create side-by-side comparison images showing before/after effects
10
- - **Base64 Support**: Handle file uploads and image responses in base64 format
11
-
12
- ## Setup
13
-
14
- ### 1. Install Dependencies
15
-
16
- ```bash
17
- pip install -r requirements.txt
18
- ```
19
-
20
- ### 2. Run the Server
21
-
22
- ```bash
23
- uvicorn main:app --reload
24
- ```
25
-
26
- The API will be available at `http://localhost:8000`
27
-
28
- ### 3. API Documentation
29
-
30
- Visit `http://localhost:8000/docs` for interactive API documentation.
31
-
32
- ## API Endpoints
33
-
34
- ### POST /transform-lut
35
-
36
- Transform a LUT file using a text prompt.
37
-
38
- **Request Body:**
39
- ```json
40
- {
41
- "cube_file_base64": "base64_encoded_cube_file",
42
- "user_prompt": "Make this LUT more cinematic with cool shadows"
43
- }
44
- ```
45
-
46
- **Response:**
47
- ```json
48
- {
49
- "success": true,
50
- "message": "LUT transformation completed successfully",
51
- "adjustments_applied": {
52
- "shadows": {"r": 0.9, "g": 1.0, "b": 1.2},
53
- "midtones": {"r": 1.0, "g": 1.0, "b": 1.0},
54
- "highlights": {"r": 1.1, "g": 1.05, "b": 0.95},
55
- "global": {"r": 1.0, "g": 1.0, "b": 1.0}
56
- },
57
- "split_preview_base64": "base64_encoded_preview_image"
58
- }
59
- ```
60
-
61
- ### GET /health
62
-
63
- Check API health and sample image availability.
64
-
65
- **Response:**
66
- ```json
67
- {
68
- "status": "healthy",
69
- "sample_image_exists": true
70
- }
71
- ```
72
-
73
- ## How It Works
74
-
75
- 1. **Upload**: Send a .cube file as base64 and a text prompt
76
- 2. **AI Processing**: The `generate_new_cube()` function processes the prompt and returns JSON adjustments
77
- 3. **LUT Transformation**: Apply the adjustments to the original LUT using the `LUTTransformer` class
78
- 4. **Image Processing**: Apply both original and modified LUTs to the sample image
79
- 5. **Split Preview**: Create a side-by-side comparison with a vertical divider line
80
- 6. **Response**: Return the preview image as base64
81
-
82
- ## LUT Adjustment Format
83
-
84
- The AI generates adjustments in this JSON format:
85
-
86
- ```json
87
- {
88
- "shadows": {"r": 0.9, "g": 1.0, "b": 1.2},
89
- "midtones": {"r": 1.0, "g": 1.0, "b": 1.0},
90
- "highlights": {"r": 1.1, "g": 1.05, "b": 0.95},
91
- "global": {"r": 1.0, "g": 1.0, "b": 1.0}
92
- }
93
- ```
94
-
95
- - **shadows**: Adjustments for darker regions (luminance < 0.33)
96
- - **midtones**: Adjustments for medium regions (0.33 ≤ luminance < 0.66)
97
- - **highlights**: Adjustments for brighter regions (luminance ≥ 0.66)
98
- - **global**: Overall adjustments applied to all regions
99
-
100
- ## Testing
101
-
102
- Use the provided `test_main.http` file to test the endpoints, or use curl:
103
-
104
- ```bash
105
- curl -X POST "http://localhost:8000/transform-lut" \
106
- -H "Content-Type: application/json" \
107
- -d '{
108
- "cube_file_base64": "VElUTEUgIlRlc3QgTFVUIgpMVVRfM0RfU0laRSAyCg...",
109
- "user_prompt": "Make this LUT more cinematic with cool shadows"
110
- }'
111
- ```
112
-
113
- ## Sample Image
114
-
115
- The API uses `sample.jpg` as the standard test image for preview generation. Make sure this file exists in the project root.
116
-
117
- ## AI Integration
118
-
119
- Replace the placeholder `generate_new_cube()` function with your actual AI implementation that takes a user prompt and returns color adjustment JSON.
120
-
121
- ## Error Handling
122
-
123
- The API includes comprehensive error handling for:
124
- - Invalid cube file formats
125
- - Missing sample images
126
- - Base64 decoding errors
127
- - Image processing failures
128
- - File system operations
 
1
+ ---
2
+ title: Lut Ai
3
+ emoji: 🏃
4
+ colorFrom: yellow
5
+ colorTo: gray
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
__pycache__/ai.cpython-312.pyc ADDED
Binary file (5.54 kB). View file
 
__pycache__/main.cpython-312.pyc ADDED
Binary file (18.9 kB). View file
 
main.py CHANGED
@@ -56,14 +56,16 @@ def init_database():
56
  conn = sqlite3.connect(DATABASE_PATH)
57
  cursor = conn.cursor()
58
 
59
- cursor.execute("""
 
60
  CREATE TABLE IF NOT EXISTS cube_files (
61
  id TEXT PRIMARY KEY,
62
  file_name TEXT NOT NULL,
63
  file_data BLOB NOT NULL,
64
  upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
65
  )
66
- """)
 
67
 
68
  conn.commit()
69
  conn.close()
@@ -77,7 +79,7 @@ def save_cube_file_to_db(file_name: str, file_data: bytes) -> str:
77
 
78
  cursor.execute(
79
  "INSERT INTO cube_files (id, file_name, file_data) VALUES (?, ?, ?)",
80
- (file_id, file_name, file_data)
81
  )
82
 
83
  conn.commit()
@@ -91,8 +93,7 @@ def get_cube_file_from_db(file_id: str) -> Optional[tuple]:
91
  cursor = conn.cursor()
92
 
93
  cursor.execute(
94
- "SELECT file_name, file_data FROM cube_files WHERE id = ?",
95
- (file_id,)
96
  )
97
 
98
  result = cursor.fetchone()
@@ -272,7 +273,7 @@ def apply_lut_to_image(image_path: str, lut_path: str) -> np.ndarray:
272
 
273
 
274
  def create_split_preview(
275
- original_lut_path: str, new_lut_path: str, sample_image_path: str
276
  ) -> str:
277
  """Create a split preview image and return as base64"""
278
  try:
@@ -318,17 +319,14 @@ async def upload_cube_file(file: UploadFile = File(...)):
318
  Upload a .cube file and save it to the database
319
  """
320
  try:
321
- if not file.filename.endswith('.cube'):
322
  raise HTTPException(status_code=400, detail="Only .cube files are allowed")
323
 
324
  file_data = await file.read()
325
 
326
  file_id = save_cube_file_to_db(file.filename, file_data)
327
 
328
- return CubeFileResponse(
329
- file_id=file_id,
330
- file_name=file.filename
331
- )
332
 
333
  except Exception as e:
334
  raise HTTPException(status_code=500, detail=f"Error uploading file: {str(e)}")
@@ -343,9 +341,7 @@ async def list_cube_files():
343
  files = list_cube_files_from_db()
344
  return [
345
  CubeFileListItem(
346
- file_id=file_id,
347
- file_name=file_name,
348
- upload_date=upload_date
349
  )
350
  for file_id, file_name, upload_date in files
351
  ]
@@ -366,7 +362,7 @@ async def transform_lut(request: LUTTransformRequest):
366
  file_name, cube_data = file_data
367
 
368
  with tempfile.NamedTemporaryFile(
369
- mode="wb", suffix=".cube", delete=False
370
  ) as temp_cube:
371
  temp_cube.write(cube_data)
372
  original_cube_path = temp_cube.name
@@ -383,12 +379,12 @@ async def transform_lut(request: LUTTransformRequest):
383
  )
384
 
385
  with tempfile.NamedTemporaryFile(
386
- mode="w", suffix=".cube", delete=False
387
  ) as temp_new_cube:
388
  new_cube_path = temp_new_cube.name
389
 
390
  if not transformer.save_cube_file(
391
- new_cube_path, f"{transformer.title}_AI_Modified"
392
  ):
393
  raise HTTPException(
394
  status_code=500, detail="Failed to save new cube file"
 
56
  conn = sqlite3.connect(DATABASE_PATH)
57
  cursor = conn.cursor()
58
 
59
+ cursor.execute(
60
+ """
61
  CREATE TABLE IF NOT EXISTS cube_files (
62
  id TEXT PRIMARY KEY,
63
  file_name TEXT NOT NULL,
64
  file_data BLOB NOT NULL,
65
  upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
66
  )
67
+ """
68
+ )
69
 
70
  conn.commit()
71
  conn.close()
 
79
 
80
  cursor.execute(
81
  "INSERT INTO cube_files (id, file_name, file_data) VALUES (?, ?, ?)",
82
+ (file_id, file_name, file_data),
83
  )
84
 
85
  conn.commit()
 
93
  cursor = conn.cursor()
94
 
95
  cursor.execute(
96
+ "SELECT file_name, file_data FROM cube_files WHERE id = ?", (file_id,)
 
97
  )
98
 
99
  result = cursor.fetchone()
 
273
 
274
 
275
  def create_split_preview(
276
+ original_lut_path: str, new_lut_path: str, sample_image_path: str
277
  ) -> str:
278
  """Create a split preview image and return as base64"""
279
  try:
 
319
  Upload a .cube file and save it to the database
320
  """
321
  try:
322
+ if not file.filename.endswith(".cube"):
323
  raise HTTPException(status_code=400, detail="Only .cube files are allowed")
324
 
325
  file_data = await file.read()
326
 
327
  file_id = save_cube_file_to_db(file.filename, file_data)
328
 
329
+ return CubeFileResponse(file_id=file_id, file_name=file.filename)
 
 
 
330
 
331
  except Exception as e:
332
  raise HTTPException(status_code=500, detail=f"Error uploading file: {str(e)}")
 
341
  files = list_cube_files_from_db()
342
  return [
343
  CubeFileListItem(
344
+ file_id=file_id, file_name=file_name, upload_date=upload_date
 
 
345
  )
346
  for file_id, file_name, upload_date in files
347
  ]
 
362
  file_name, cube_data = file_data
363
 
364
  with tempfile.NamedTemporaryFile(
365
+ mode="wb", suffix=".cube", delete=False
366
  ) as temp_cube:
367
  temp_cube.write(cube_data)
368
  original_cube_path = temp_cube.name
 
379
  )
380
 
381
  with tempfile.NamedTemporaryFile(
382
+ mode="w", suffix=".cube", delete=False
383
  ) as temp_new_cube:
384
  new_cube_path = temp_new_cube.name
385
 
386
  if not transformer.save_cube_file(
387
+ new_cube_path, f"{transformer.title}_AI_Modified"
388
  ):
389
  raise HTTPException(
390
  status_code=500, detail="Failed to save new cube file"