fsdfsdfsdfsdfdas commited on
Commit
c4b7f45
·
verified ·
1 Parent(s): 594c999

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +218 -0
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ import requests
3
+ import base64
4
+ import io
5
+ import json
6
+ from urllib.parse import unquote
7
+
8
+ app = Flask(__name__)
9
+
10
+ # Style configurations
11
+ STYLES = {
12
+ 'pixel': {
13
+ 'prompt': 'Turn this image into the Pixel style.',
14
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/Pixel_lora_weights.safetensors'
15
+ },
16
+ 'snoopy': {
17
+ 'prompt': 'Turn this image into the Snoopy style.',
18
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/Snoopy_lora_weights.safetensors'
19
+ },
20
+ 'jojo': {
21
+ 'prompt': 'Turn this image into the JoJo style.',
22
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/Jojo_lora_weights.safetensors'
23
+ },
24
+ 'clay': {
25
+ 'prompt': 'Turn this image into the Clay style.',
26
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/Clay_Toy_lora_weights.safetensors'
27
+ },
28
+ 'ghibli': {
29
+ 'prompt': 'Turn this image into the Ghibli style.',
30
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/Ghibli_lora_weights.safetensors'
31
+ },
32
+ 'american-cartoon': {
33
+ 'prompt': 'Turn this image into the American Cartoon style.',
34
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/American_Cartoon_lora_weights.safetensors'
35
+ },
36
+ 'lego': {
37
+ 'prompt': 'convert to lego style',
38
+ 'lora_url': 'https://huggingface.co/Owen777/Kontext-Style-Loras/resolve/main/LEGO_lora_weights.safetensors'
39
+ },
40
+ 'broccoli-hair': {
41
+ 'prompt': 'Change hair to a broccoli haircut',
42
+ 'lora_url': 'https://huggingface.co/fal/Broccoli-Hair-Kontext-Dev-LoRA/resolve/main/broccoli-hair-kontext-dev-lora.safetensors'
43
+ },
44
+ 'plushie': {
45
+ 'prompt': 'Convert to plushie style',
46
+ 'lora_url': 'https://huggingface.co/fal/Plushie-Kontext-Dev-LoRA/resolve/main/plushie-kontext-dev-lora.safetensors'
47
+ },
48
+ 'wojak': {
49
+ 'prompt': 'Convert to wojak style drawing',
50
+ 'lora_url': 'https://huggingface.co/fal/Wojak-Kontext-Dev-LoRA/resolve/main/wojak-kontext-dev-lora.safetensors'
51
+ },
52
+ 'upscalecompression': {
53
+ 'prompt': 'fix the jpeg compression',
54
+ 'lora_url': 'https://huggingface.co/fofr/flux-kontext-dev-jpeg-compression-fix-lora/resolve/main/flux-kontext-dev-jpeg-compression-fix-lora.safetensors'
55
+ },
56
+ 'fluffy': {
57
+ 'prompt': 'make this object fluffy',
58
+ 'lora_url': None
59
+ },
60
+ 'glass': {
61
+ 'prompt': 'make the character/object look like it was made out of glass, black background',
62
+ 'lora_url': None
63
+ },
64
+ 'simpsons': {
65
+ 'prompt': 'convert to Simpsons cartoon style',
66
+ 'lora_url': None
67
+ },
68
+ 'anime': {
69
+ 'prompt': 'convert to anime art style with large eyes and stylized features',
70
+ 'lora_url': None
71
+ }
72
+ }
73
+
74
+ def upload_base64_image(base64_data):
75
+ """Upload base64 image to jerrrycans-file.hf.space"""
76
+ try:
77
+ # Convert base64 to bytes
78
+ header, data = base64_data.split(',', 1)
79
+ image_data = base64.b64decode(data)
80
+
81
+ # Upload to jerrrycans
82
+ files = {'file': ('generated_image.png', io.BytesIO(image_data), 'image/png')}
83
+ headers = {
84
+ 'Origin': 'https://jerrrycans-file.hf.space',
85
+ 'Referer': 'https://jerrrycans-file.hf.space/',
86
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
87
+ }
88
+
89
+ response = requests.post('https://jerrrycans-file.hf.space/upload',
90
+ files=files, headers=headers)
91
+
92
+ if response.status_code == 200:
93
+ result = response.json()
94
+ return f"https://jerrrycans-file.hf.space{result['url']}"
95
+ except Exception as e:
96
+ print(f"Upload error: {e}")
97
+ return None
98
+
99
+ def get_style_config(style, custom_lora_url=None):
100
+ """Get style configuration"""
101
+ style_config = STYLES.get(style.lower())
102
+
103
+ if style_config:
104
+ return {
105
+ 'prompt': style_config['prompt'],
106
+ 'lora_url': custom_lora_url or style_config['lora_url']
107
+ }
108
+ else:
109
+ # Custom prompt
110
+ return {
111
+ 'prompt': style,
112
+ 'lora_url': custom_lora_url
113
+ }
114
+
115
+ def process_stream_response(response):
116
+ """Process streaming response from FAL API"""
117
+ uploaded_images = []
118
+ buffer = ''
119
+
120
+ for chunk in response.iter_content(chunk_size=1024, decode_unicode=True):
121
+ if chunk:
122
+ buffer += chunk
123
+ lines = buffer.split('\n')
124
+ buffer = lines.pop() # Keep incomplete line in buffer
125
+
126
+ for line in lines:
127
+ if line.startswith('data: ') and len(line) > 6:
128
+ try:
129
+ data_content = line[6:].strip()
130
+ if not data_content:
131
+ continue
132
+
133
+ data = json.loads(data_content)
134
+
135
+ # Check for progress or completed data with images
136
+ if (data.get('json', {}).get('type') in ['progress', 'completed'] and
137
+ data.get('json', {}).get('data', {}).get('images')):
138
+
139
+ for image in data['json']['data']['images']:
140
+ if image.get('url', '').startswith('data:image/'):
141
+ uploaded_url = upload_base64_image(image['url'])
142
+ if uploaded_url:
143
+ uploaded_images.append(uploaded_url)
144
+ except:
145
+ continue
146
+
147
+ return uploaded_images
148
+
149
+ @app.route('/api/transform', methods=['POST'])
150
+ def transform_image():
151
+ try:
152
+ # Get JSON data from request
153
+ data = request.get_json()
154
+
155
+ if not data:
156
+ return jsonify({'success': False, 'error': 'No JSON data provided'}), 400
157
+
158
+ # Extract required fields
159
+ image_url = data.get('image_url')
160
+ style = data.get('style')
161
+ custom_lora_url = data.get('lora_url')
162
+
163
+ # Validate required fields
164
+ if not image_url:
165
+ return jsonify({'success': False, 'error': 'image_url is required'}), 400
166
+
167
+ if not style:
168
+ return jsonify({'success': False, 'error': 'style is required'}), 400
169
+
170
+ # Validate image URL
171
+ if not image_url.startswith(('http://', 'https://')):
172
+ return jsonify({'success': False, 'error': 'Invalid image URL'}), 400
173
+
174
+ # Get style configuration
175
+ style_config = get_style_config(style, custom_lora_url)
176
+
177
+ # Prepare generation parameters
178
+ generate_params = {
179
+ 'json': {
180
+ 'imageUrl': image_url,
181
+ 'prompt': style_config['prompt']
182
+ }
183
+ }
184
+
185
+ if style_config['lora_url']:
186
+ generate_params['json']['loraUrl'] = style_config['lora_url']
187
+
188
+ # Make request to FAL API
189
+ generate_url = f"https://fal-kontext-demo.vercel.app/api/trpc/generateImageStream?input={requests.utils.quote(json.dumps(generate_params))}"
190
+
191
+ response = requests.get(generate_url, stream=True)
192
+ if response.status_code != 200:
193
+ return jsonify({'success': False, 'error': 'Failed to generate styled image'}), 500
194
+
195
+ # Process streaming response
196
+ uploaded_images = process_stream_response(response)
197
+
198
+ return jsonify({
199
+ 'success': True,
200
+ 'originalImage': image_url,
201
+ 'style': style,
202
+ 'generatedImages': uploaded_images,
203
+ 'count': len(uploaded_images)
204
+ })
205
+
206
+ except Exception as e:
207
+ return jsonify({'success': False, 'error': str(e)}), 500
208
+
209
+ @app.route('/')
210
+ def index():
211
+ return jsonify({
212
+ 'message': 'Image Style Transfer API',
213
+ 'usage': 'POST /api/transform with JSON body: {"image_url": "...", "style": "...", "lora_url": "..."}',
214
+ 'styles': list(STYLES.keys())
215
+ })
216
+
217
+ if __name__ == '__main__':
218
+ app.run(host='0.0.0.0', port=7860, debug=True)