AItool commited on
Commit
d8134d9
·
verified ·
1 Parent(s): 8228e46

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -214
app.py CHANGED
@@ -1,108 +1,52 @@
1
- from fastapi import FastAPI, File, UploadFile, Form
2
  from fastapi.responses import HTMLResponse
3
  from fastapi.staticfiles import StaticFiles
4
- import numpy as np
5
  from PIL import Image
6
- from io import BytesIO
7
  import requests
 
8
  import base64
9
- import os
10
- from tkinter import Tk, Label, Button, Radiobutton, IntVar
11
 
12
  app = FastAPI()
13
 
14
- # Mount the static folder for CSS and other assets
15
  app.mount("/static", StaticFiles(directory="static"), name="static")
16
 
17
- # Function to add padding
18
- def fill_rectangle_cropper(img, padding_type):
19
- # Calculate the average color of the image
20
- avg_color_per_row = np.average(np.array(img), axis=0)
21
- avg_color = np.average(avg_color_per_row, axis=0)
22
-
23
- if padding_type == "top_bottom":
24
- # Increase height to create a rectangle
25
- new_height = int(img.width * (4/3)) # Example: height = width * 4/3
26
- newimg = Image.new(
27
- 'RGB',
28
- (img.width, new_height),
29
- (round(avg_color[0]), round(avg_color[1]), round(avg_color[2]))
30
- )
31
- padding_top = (new_height - img.height) // 2
32
- newimg.paste(img, (0, padding_top)) # Center the image vertically
33
- return newimg
34
-
35
- elif padding_type == "left_right":
36
- # Increase width to create a rectangle
37
- new_width = int(img.height * (4/3)) # Example: width = height * 4/3
38
- newimg = Image.new(
39
- 'RGB',
40
- (new_width, img.height),
41
- (round(avg_color[0]), round(avg_color[1]), round(avg_color[2]))
42
- )
43
- padding_left = (new_width - img.width) // 2
44
- newimg.paste(img, (padding_left, 0)) # Center the image horizontally
45
- return newimg
46
 
47
  # Home Page
48
  @app.get("/", response_class=HTMLResponse)
49
  def home_page():
50
  return """
51
  <html>
52
- <head>
53
- <title>Part of Idoia's Developer Portfolio - Innovating the Web</title>
54
- <link rel="stylesheet" href="/static/styles/style.css">
55
-
56
- <!-- Meta Tags for SEO -->
57
- <meta charset="UTF-8">
58
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
59
- <meta name="description" content="Explore the developer portfolio of Idoia, showcasing expertise in FastAPI, web development, and cutting-edge applications.">
60
- <meta name="keywords" content="Idoia, Developer, FastAPI, Web Development, Python Projects, Image Processing, Online Portfolio">
61
- <meta name="author" content="Idoia">
62
-
63
- <!-- Open Graph Meta Tags -->
64
- <meta property="og:title" content="Idoia's Developer Portfolio - Innovating the Web">
65
- <meta property="og:description" content="Showcasing FastAPI projects, web apps, and image processing expertise. Explore Idoia's developer journey.">
66
- <meta property="og:image" content="/static/images/banner.jpg">
67
- <meta property="og:url" content="https://webdevserv.github.io/html_bites/dev/webdev.html">
68
- <meta property="og:type" content="website">
69
-
70
- <!-- Twitter Card Meta Tags -->
71
- <meta name="twitter:card" content="summary_large_image">
72
- <meta name="twitter:title" content="Idoia's Developer Portfolio - Innovating the Web">
73
- <meta name="twitter:description" content="Discover the developer profile of Idoia. Dive into FastAPI-powered web apps and creative Python projects.">
74
- <meta name="twitter:image" content="/static/images/banner.jpg">
75
- <link rel="icon" href="/static/images/6464.ico" type="image/x-icon">
76
-
77
- <!-- Google Fonts (Optional for Styling) -->
78
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
79
-
80
- <!-- Schema.org JSON-LD (Optional for Rich Snippets) -->
81
- <script type="application/ld+json">
82
- {
83
- "@context": "https://schema.org",
84
- "@type": "Person",
85
- "name": "Idoia",
86
- "jobTitle": "Web Developer",
87
- "url": "https://webdevserv.github.io/html_bites/dev/webdev.html",
88
- "image": "https://idoia-dev-portfolio.com/static/images/banner.jpg",
89
- "description": "Experienced web developer with a focus on Streamlit, HF, Python, and modern web applications."
90
- }
91
- </script>
92
- </head>
93
  <body>
94
- <img class="banner" src="/static/images/banner.jpg" alt="Banner" width="100%">
95
- <h2>Rectangle and Fill Image App</h2>
96
- <p>Please select an option below:</p>
97
- <ul>
98
- <li><a href="/demo">Demo</a></li>
99
- <li><a href="/application">Application</a></li>
100
- </ul>
101
- <div id="credit">Image credit
102
- <a href="https://stock.adobe.com/es/contributor/212598146/UMAMI%20LAB" target="_blank">Adobe Stock User Umami Lab</a>
103
- and
104
- <a href="https://www.shutterstock.com/g/Idoia+Lerchundi?rid=430751957" target="_blank">Shutterstock User PhoArt101</a>.
105
- </div>
106
  </body>
107
  </html>
108
  """
@@ -110,148 +54,35 @@ def home_page():
110
  # Demo Page
111
  @app.get("/demo", response_class=HTMLResponse)
112
  def demo_page():
113
- # URLs for demo images
114
  url1 = "https://raw.githubusercontent.com/webdevserv/images_video/main/squareportrait.png"
115
  url2 = "https://raw.githubusercontent.com/webdevserv/images_video/main/squarelandscape.png"
116
 
117
- # Process the first image
 
 
 
118
  response = requests.get(url1)
119
  img1 = Image.open(BytesIO(response.content)).convert("RGB")
120
- rectangled_img1 = fill_rectangle_cropper(img1, "top_bottom")
121
  output1 = BytesIO()
122
- rectangled_img1.save(output1, format="JPEG")
123
  encoded_img1 = base64.b64encode(output1.getvalue()).decode("utf-8")
124
 
125
- # Process the second image
126
  response = requests.get(url2)
127
  img2 = Image.open(BytesIO(response.content)).convert("RGB")
128
- rectangled_img2 = fill_rectangle_cropper(img2, "left_right")
129
  output2 = BytesIO()
130
- rectangled_img2.save(output2, format="JPEG")
131
  encoded_img2 = base64.b64encode(output2.getvalue()).decode("utf-8")
132
 
133
  return f"""
134
  <html>
135
- <head>
136
- <title>Part of Idoia's Developer Portfolio - Innovating the Web</title>
137
- <link rel="stylesheet" href="/static/styles/style.css">
138
- </head>
139
- <body>
140
- <img class="banner" src="/static/images/banner.jpg" alt="Banner" width="100%">
141
- <h2>Rectangle Image Demo (CPU Optimized)</h2>
142
- <p>Image will be rectangled with color filler where applicable.</p>
143
- <h3>Result 1:</h3>
144
- <img src="data:image/jpeg;base64,{encoded_img1}" />
145
- <h3>Result 2:</h3>
146
- <img src="data:image/jpeg;base64,{encoded_img2}" />
147
- <p><a href="/">Back</a></p>
148
- <div id="credit">Image credit
149
- <a href="https://stock.adobe.com/es/contributor/212598146/UMAMI%20LAB" target="_blank">Adobe Stock User Umami Lab</a>
150
- and
151
- <a href="https://www.shutterstock.com/g/Idoia+Lerchundi?rid=430751957" target="_blank">Shutterstock User PhoArt101</a>.
152
- </div>
153
- </body>
154
- </html>
155
- """
156
-
157
- # Application Page
158
- @app.get("/application", response_class=HTMLResponse)
159
- def application_page():
160
- return """
161
- <html>
162
- <head>
163
- <title>Part of Idoia's Developer Portfolio - Innovating the Web</title>
164
- <link rel="stylesheet" href="/static/styles/style.css">
165
- </head>
166
  <body>
167
- <img class="banner" src="/static/images/banner.jpg" alt="Banner" width="100%">
168
- <h2>Rectangle Image Application (CPU Optimized)</h2>
169
- <p>Upload a JPG image to rectangle and fill with color filler. Suitable for youtube shorts, instagram reels and TikTok.</p>
170
- <form action="/upload/" enctype="multipart/form-data" method="post">
171
- <label for="file">Upload your image:</label>
172
- <input name="file" type="file" required><br><br>
173
-
174
- <label>Choose the padding direction:</label><br>
175
- <input type="radio" id="top_bottom" name="padding_type" value="top_bottom" checked>
176
- <label for="top_bottom">Top/Bottom</label><br>
177
- <input type="radio" id="left_right" name="padding_type" value="left_right">
178
- <label for="left_right">Left/Right</label><br><br>
179
-
180
- <input type="submit" value="Rectangle It">
181
- </form>
182
- <a href="/">Back</a>
183
- <div id="credit">Image credit
184
- <a href="https://stock.adobe.com/es/contributor/212598146/UMAMI%20LAB" target="_blank">Adobe Stock User Umami Lab</a>
185
- and
186
- <a href="https://www.shutterstock.com/g/Idoia+Lerchundi?rid=430751957" target="_blank">Shutterstock User PhoArt101</a>.
187
- </div>
188
  </body>
189
  </html>
190
  """
191
-
192
- @app.post("/upload/")
193
- async def upload_file(file: UploadFile = File(...), padding_type: str = Form(...)):
194
- try:
195
- # Await file upload
196
- contents = await file.read()
197
- img = Image.open(BytesIO(contents)).convert("RGB")
198
- # Apply padding based on user's choice
199
- rectangled_img = fill_rectangle_cropper(img,padding_type)
200
-
201
- # Save the rectangle image (original size)
202
- output = BytesIO()
203
- rectangled_img.save(output, format="JPEG")
204
- output.seek(0)
205
-
206
- # Encode the full-size image for download
207
- full_size_encoded_img = base64.b64encode(output.getvalue()).decode("utf-8")
208
-
209
- # Resize the image for display (512px by 512px)
210
- display_img = rectangled_img.copy()
211
- desired_width = 512
212
- aspect_ratio = display_img.height / display_img.width
213
- desired_height = int(desired_width * aspect_ratio)
214
- display_img.thumbnail((desired_width, desired_height))
215
- display_output = BytesIO()
216
- display_img.save(display_output, format="JPEG")
217
- display_output.seek(0)
218
-
219
- # Encode the resized display image
220
- display_encoded_img = base64.b64encode(display_output.getvalue()).decode("utf-8")
221
-
222
- # Return the HTML response
223
- return HTMLResponse(
224
- content=f"""
225
- <html>
226
- <head>
227
- <title>Part of Idoia's Developer Portfolio - Innovating the Web</title>
228
- <link rel="stylesheet" href="/static/styles/style.css">
229
- </head>
230
- <body>
231
- <img class="banner" src="/static/images/banner.jpg" alt="Banner" width="100%">
232
- <h2>Image successfully rectangled!</h2>
233
- <!--<img src='data:image/jpeg;base64,display_encoded_img'/>-->
234
- <img src='data:image/jpeg;base64,{display_encoded_img}' width="512"/>
235
- <p><a href="data:image/jpeg;base64,{full_size_encoded_img}" download="rectangled_image.jpg">
236
- Download Full-Size Image</a></p>
237
- <div style="margin: 0.75em 0;"><a href="https://www.buymeacoffee.com/Artgen" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a></div>
238
- <div style="margin: 0.75em 0;">But what would really help me is a <strong>PRO subscription</strong> to Google Colab, Kaggle or Hugging Face. Many thanks.</div>
239
- <p><a href="/">Back</a></p>
240
- <div id="credit">Image credit
241
- <a href="https://stock.adobe.com/es/contributor/212598146/UMAMI%20LAB" target="_blank">Adobe Stock User Umami Lab</a>
242
- and
243
- <a href="https://www.shutterstock.com/g/Idoia+Lerchundi?rid=430751957" target="_blank">Shutterstock User PhoArt101</a>.
244
- </div>
245
- <div style="margin: 0.75em 0;"><a href="https://www.buymeacoffee.com/Artgen" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a></div>
246
- <div style="margin: 0.75em 0;">But what would really help me is a <strong>PRO subscription</strong> to Google Colab, Kaggle or Hugging Face. Many thanks.</div>
247
- </body>
248
- </html>
249
- """,
250
- media_type="text/html"
251
- )
252
- except Exception as e:
253
- return HTMLResponse(content=f"<h3>An error occurred: {e}</h3>", media_type="text/html")
254
-
255
- if __name__ == "__main__":
256
- import uvicorn
257
- uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
 
1
+ from fastapi import FastAPI
2
  from fastapi.responses import HTMLResponse
3
  from fastapi.staticfiles import StaticFiles
 
4
  from PIL import Image
 
5
  import requests
6
+ from io import BytesIO
7
  import base64
 
 
8
 
9
  app = FastAPI()
10
 
11
+ # Mount static folder
12
  app.mount("/static", StaticFiles(directory="static"), name="static")
13
 
14
+ # Function to crop to desired dimensions while keeping ratio
15
+ def crop_to_dimensions(img: Image.Image, target_width: int, target_height: int) -> Image.Image:
16
+ # Original size
17
+ orig_w, orig_h = img.size
18
+ target_ratio = target_width / target_height
19
+ orig_ratio = orig_w / orig_h
20
+
21
+ # Scale image to cover target box
22
+ if orig_ratio > target_ratio:
23
+ # Image is wider than target → scale by height
24
+ new_h = target_height
25
+ new_w = int(orig_w * (target_height / orig_h))
26
+ else:
27
+ # Image is taller/narrower → scale by width
28
+ new_w = target_width
29
+ new_h = int(orig_h * (target_width / orig_w))
30
+
31
+ img_resized = img.resize((new_w, new_h), Image.LANCZOS)
32
+
33
+ # Crop center
34
+ left = (new_w - target_width) // 2
35
+ top = (new_h - target_height) // 2
36
+ right = left + target_width
37
+ bottom = top + target_height
38
+
39
+ return img_resized.crop((left, top, right, bottom))
 
 
 
40
 
41
  # Home Page
42
  @app.get("/", response_class=HTMLResponse)
43
  def home_page():
44
  return """
45
  <html>
46
+ <head><title>Image Cropper</title></head>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  <body>
48
+ <h2>Crop Image App</h2>
49
+ <p>Try the <a href="/demo">demo</a>.</p>
 
 
 
 
 
 
 
 
 
 
50
  </body>
51
  </html>
52
  """
 
54
  # Demo Page
55
  @app.get("/demo", response_class=HTMLResponse)
56
  def demo_page():
 
57
  url1 = "https://raw.githubusercontent.com/webdevserv/images_video/main/squareportrait.png"
58
  url2 = "https://raw.githubusercontent.com/webdevserv/images_video/main/squarelandscape.png"
59
 
60
+ # Example target dimensions
61
+ target_w, target_h = 400, 300
62
+
63
+ # Process first image
64
  response = requests.get(url1)
65
  img1 = Image.open(BytesIO(response.content)).convert("RGB")
66
+ cropped1 = crop_to_dimensions(img1, target_w, target_h)
67
  output1 = BytesIO()
68
+ cropped1.save(output1, format="JPEG")
69
  encoded_img1 = base64.b64encode(output1.getvalue()).decode("utf-8")
70
 
71
+ # Process second image
72
  response = requests.get(url2)
73
  img2 = Image.open(BytesIO(response.content)).convert("RGB")
74
+ cropped2 = crop_to_dimensions(img2, target_w, target_h)
75
  output2 = BytesIO()
76
+ cropped2.save(output2, format="JPEG")
77
  encoded_img2 = base64.b64encode(output2.getvalue()).decode("utf-8")
78
 
79
  return f"""
80
  <html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  <body>
82
+ <h2>Cropped Images</h2>
83
+ <p>Target size: {target_w}x{target_h}</p>
84
+ <img src="data:image/jpeg;base64,{encoded_img1}" alt="Cropped1"><br>
85
+ <img src="data:image/jpeg;base64,{encoded_img2}" alt="Cropped2">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  </body>
87
  </html>
88
  """