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