Suprhimp commited on
Commit
3450e6b
1 Parent(s): 2262b5b

first commit

Browse files
Files changed (8) hide show
  1. Dockerfile +11 -0
  2. main.py +42 -0
  3. opengl.py +412 -0
  4. requirments.txt +7 -0
  5. utils/ColorMatrix.py +234 -0
  6. utils/dto.py +17 -0
  7. utils/s3.py +14 -0
  8. utils/settings.py +77 -0
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ COPY . .
10
+
11
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", "--worker", "2"]
main.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Annotated
2
+
3
+ from fastapi import FastAPI, File, HTTPException
4
+ from fastapi.responses import StreamingResponse
5
+ from starlette.middleware.cors import CORSMiddleware
6
+ from opengl import image_enhance
7
+
8
+ from utils.dto import ToneUpData
9
+ from utils.s3 import get_image_from_s3
10
+
11
+
12
+ app = FastAPI()
13
+ cors_options = {
14
+ "allow_methods": ["*"],
15
+ "allow_headers": ["*"],
16
+ "allow_credentials": True,
17
+ "allow_origins": [
18
+ "https://www.photio.io",
19
+ "https://dev.photio.io",
20
+ "http://localhost:3000",
21
+ "http://localhost",
22
+ "http://172.30.1.10:3000",
23
+ ],
24
+ }
25
+ app.add_middleware(CORSMiddleware, **cors_options)
26
+
27
+
28
+ @app.get("/")
29
+ def read_root():
30
+ return {"Hello": "World!"}
31
+
32
+
33
+ @app.post("/tone-up")
34
+ async def remove_background(items:ToneUpData):
35
+ try:
36
+ image = get_image_from_s3(items.image_id)
37
+ out_image = image_enhance(image,items.exposure,items.saturation,items.contrast,items.brightness,items.gamma,items.shadows,items.highlights,items.whites,items.blacks,items.clarity,items.temperature,items.sharpness)
38
+ # csv_to_r2(output_image,image_id)
39
+ return {"result": out_image}
40
+ except RuntimeError as e:
41
+ raise HTTPException(status_code=422, detail=f"error occur: {e}") from e
42
+
opengl.py ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """
4
+ The OpenGL specification doesn't allow you to create a context without a window,
5
+ since it needs the pixel format that you set into the device context.
6
+ Actually, it is necessary to have a window handler to create a "traditional" rendering context.
7
+ It is used to fetch OpenGL information and extensions availability.
8
+ Once you got that information, you can destroy the render context and release the "dummy" window.
9
+ So, in this code, the window is created, the context is set to this window,
10
+ the image result is saved to an output image file and, then, this window is released.
11
+ """
12
+
13
+
14
+ import glfw
15
+ from OpenGL.GL import *
16
+ import OpenGL.GL.shaders
17
+ import numpy
18
+ from PIL import Image
19
+ import base64
20
+ from io import BytesIO
21
+ from utils.settings import set_options
22
+
23
+
24
+ def image_enhance(image, exposure, saturation, contrast, brightness, gamma, shadows, highlights, whites, blacks,
25
+ clarity, temperature, sharpness):
26
+ # Initialize glfw
27
+ if not glfw.init():
28
+ print('error in init')
29
+ return
30
+
31
+ # Create window
32
+ # Size (1, 1) for show nothing in window
33
+ window = glfw.create_window(1, 1, "My OpenGL window", None, None)
34
+ # window = glfw.create_window(800, 600, "My OpenGL window", None, None)
35
+
36
+ # Terminate if any issue
37
+ if not window:
38
+ print('error in window')
39
+ glfw.terminate()
40
+ return
41
+
42
+ # Set context to window
43
+ glfw.make_context_current(window)
44
+
45
+ #
46
+
47
+ # Initial data
48
+ # Positions, colors, texture coordinates
49
+ '''
50
+ # positions colors texture coords
51
+ quad = [ -0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
52
+ 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0,
53
+ 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
54
+ -0.5, 0.5, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0]
55
+ '''
56
+ # positions colors texture coords
57
+ quad = [-1., -1., 0., 0.,
58
+ 1., -1., 1., 0.,
59
+ 1., 1., 1., 1.,
60
+ -1., 1., 0., 1.]
61
+ quad = numpy.array(quad, dtype=numpy.float32)
62
+
63
+ # Vertices indices order
64
+ indices = [0, 1, 2,
65
+ 2, 3, 0]
66
+ indices = numpy.array(indices, dtype=numpy.uint32)
67
+
68
+ # print(quad.itemsize * len(quad))
69
+ # print(indices.itemsize * len(indices))
70
+ # print(quad.itemsize * 8)
71
+
72
+ #
73
+
74
+ # Vertex shader
75
+ vertex_shader = """
76
+ attribute vec4 a_position;
77
+ attribute vec4 a_color;
78
+ attribute vec2 a_texCoord;
79
+ varying vec2 v_texCoord;
80
+ varying vec4 v_color;
81
+
82
+ void main() {
83
+ gl_Position = a_position;
84
+ v_texCoord = a_texCoord;
85
+ v_color = vec4(a_color.rgb * a_color.a, a_color.a);
86
+ }
87
+ """
88
+
89
+ # Fragment shader
90
+ fragment_shader = """
91
+ varying vec2 v_texCoord;
92
+ uniform sampler2D u_image;
93
+
94
+ uniform float u_gamma;
95
+ uniform float u_shadows;
96
+ uniform float u_highlights;
97
+ uniform float u_whites;
98
+ uniform float u_blacks;
99
+
100
+ uniform float u_clarity;
101
+
102
+
103
+ uniform mat4 u_colorMatrix;
104
+ uniform vec4 u_colorOffset;
105
+
106
+ uniform vec2 u_pixelDimension;
107
+
108
+ uniform mat4 u_clarityMatrix;
109
+ uniform vec4 u_clarityOffset;
110
+
111
+ uniform float u_temperature;
112
+ uniform float u_sharpness;
113
+
114
+
115
+ const vec3 warmFilter = vec3(0.93, 0.54, 0.0);
116
+
117
+ const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.596, -0.274, -0.322, 0.212, -0.523, 0.311);
118
+ const mat3 YIQtoRGB = mat3(1.0, 0.956, 0.621, 1.0, -0.272, -0.647, 1.0, -1.105, 1.702);
119
+
120
+
121
+ const float EPSILON = 0.0000001;
122
+
123
+ vec4 unpremultiply(vec4 col) {
124
+ col.rgb /= max(col.a, EPSILON);
125
+ return col;
126
+ }
127
+
128
+ float calculateLuminance(vec3 rgb) {
129
+ // This is the luminance calculation part of the RGB to HSL formular.
130
+ vec4 p = mix(
131
+ vec4(rgb.gb, 0.0, -1.0 / 3.0),
132
+ vec4(rgb.bg, -1.0, 2.0 / 3.0),
133
+ vec4(rgb.g < rgb.b)
134
+ );
135
+
136
+ vec4 q = mix(
137
+ vec4(rgb.r, p.yzx),
138
+ vec4(p.xyw, rgb.r),
139
+ vec4(rgb.r < p.x)
140
+ );
141
+
142
+ float croma = q.x - min(q.w, q.y);
143
+ float luminance = q.x - croma * 0.5;
144
+ return luminance;
145
+ }
146
+
147
+ vec3 map(vec3 x, float in_min, float in_max, float out_min, float out_max){
148
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
149
+ }
150
+
151
+ void main() {
152
+
153
+ vec4 color = clamp(texture2D(u_image, v_texCoord), 0.0, 1.0);
154
+ color.rgb /= max(color.a, EPSILON); // Revert premultiplied alpha
155
+
156
+ // Apply gamma
157
+ if (u_gamma != 1.0) {
158
+ color.rgb = pow(color.rgb, vec3(1.0 / max(u_gamma, EPSILON)));
159
+ }
160
+
161
+ // Apply shadows and highlights
162
+ float luminance = calculateLuminance(color.rgb);
163
+
164
+ float shadow = u_shadows >= 0.0
165
+ ? clamp(
166
+ pow(luminance, 1.0 / (u_shadows + 1.0))
167
+ + pow(luminance, 2.0 / (u_shadows + 1.0)) * -0.76
168
+ - luminance
169
+ , 0.0, max(u_shadows, 1.0))
170
+ : -clamp(
171
+ pow(luminance, 1.0 / (-u_shadows + 1.0))
172
+ + pow(luminance, 2.0 / (-u_shadows + 1.0)) * -0.76
173
+ - luminance
174
+ , 0.0, max(-u_shadows, 1.0));
175
+
176
+ float highlight = u_highlights < 0.0
177
+ ? clamp(
178
+ 1.0
179
+ - pow(1.0 - luminance, 1.0 / (1.0 - u_highlights))
180
+ - pow(1.0 - luminance, 2.0 / (1.0 - u_highlights)) * -0.8
181
+ - luminance
182
+ , -1.0, 0.0)
183
+ : -clamp(
184
+ 1.0
185
+ - pow(1.0 - luminance, 1.0 / (1.0 + u_highlights))
186
+ - pow(1.0 - luminance, 2.0 / (1.0 + u_highlights)) * -0.8
187
+ - luminance
188
+ , -1.0, 0.0);
189
+
190
+ // Bright color need more contrast and dark color need more brightness.
191
+ // This is to keep saturatation because the color information of a dark colors is lost.
192
+ float shadowContrast = shadow * luminance * luminance;
193
+ float shadowBrightness = shadow - shadowContrast;
194
+
195
+ float offset = luminance + shadowContrast + highlight;
196
+ color.rgb = clamp(offset * ((color.rgb + shadowBrightness) / max(luminance, EPSILON)), 0.0, 1.0);
197
+
198
+ // Apply Color Matrix
199
+ color.rgb = clamp(color * u_colorMatrix + u_colorOffset, 0.0, 1.0).rgb;
200
+ color.rgb = map(color.rgb, 0.0, 1.0, u_blacks / 2.0, 1.0 + u_whites / 2.0);
201
+ color = clamp(color, 0.0, 1.0);
202
+ color.rgb *= color.a; // Reset premultiplied alpha
203
+
204
+ if (u_clarity != 0.0) {
205
+ color = unpremultiply(color);
206
+
207
+ // L = Left, R = Right, C = Center, T = Top, B = Bottom
208
+ vec4 colLB = texture2D(u_image, v_texCoord + vec2(-u_pixelDimension.x, -u_pixelDimension.y));
209
+ vec4 colLC = texture2D(u_image, v_texCoord + vec2(-u_pixelDimension.x, 0.0));
210
+ vec4 colLT = texture2D(u_image, v_texCoord + vec2(-u_pixelDimension.x, u_pixelDimension.y));
211
+
212
+ vec4 colCL = texture2D(u_image, v_texCoord + vec2( 0.0, -u_pixelDimension.y));
213
+ vec4 colCR = texture2D(u_image, v_texCoord + vec2( 0.0, u_pixelDimension.y));
214
+
215
+ vec4 colRB = texture2D(u_image, v_texCoord + vec2( u_pixelDimension.x, -u_pixelDimension.y));
216
+ vec4 colRC = texture2D(u_image, v_texCoord + vec2( u_pixelDimension.x, 0.0));
217
+ vec4 colRT = texture2D(u_image, v_texCoord + vec2( u_pixelDimension.x, u_pixelDimension.y));
218
+
219
+ vec4 mergedColor = color;
220
+ mergedColor.rgb += unpremultiply(colLB).rgb + unpremultiply(colLC).rgb + unpremultiply(colLT).rgb;
221
+ mergedColor.rgb += unpremultiply(colCL).rgb + unpremultiply(colCR).rgb;
222
+ mergedColor.rgb += unpremultiply(colRB).rgb + unpremultiply(colRC).rgb + unpremultiply(colRT).rgb;
223
+
224
+ mergedColor /= 9.0;
225
+
226
+ float grayValue = clamp(color.r * 0.3 + color.g * 0.59 + color.b * 0.1, 0.111111, 0.999999);
227
+ // 1.0 and 0.0 result in white not black, therefore we clamp
228
+
229
+ // Here we create a function that will map values below 0.1 to 0. Values above 0.2 will be mapped to 1,
230
+ // and for values between 0.1 and 0.2 it will produce a gradient.
231
+ // The funtion is mirror at 0.5, meaning values between 0.8 and 0.9 will result in a decending gradient.
232
+ // And values above 0.9 will be mapped to 0.
233
+ float frequenceFactor = min(smoothstep(1.0 - grayValue, 0.0, 0.11), smoothstep(grayValue, 0.0, 0.11));
234
+
235
+ // here we apply the high pass filter. Its strength is determined by the uniform ,
236
+ // and the frequence factor. That means the only the mid tones are affected by this filter.
237
+ // Clarity input is ranging from -1 to 1. But we want to strengthen the effect.
238
+ // Therefore we see this little magic number '3.7'.
239
+ color.rgb = clamp(color + clamp((color - mergedColor) * u_clarity * 3.7 * frequenceFactor, 0.0, 10.0), 0.0, 1.0).rgb;
240
+
241
+ // apply exposure but only to the mid tones.
242
+ color.rgb = color.rgb * pow(2.0, u_clarity * 0.27 * frequenceFactor);
243
+
244
+ // apply contrast and desaturation matrix
245
+ color.rgb = clamp(color * u_colorMatrix + u_colorOffset, 0.0, 1.0).rgb;
246
+
247
+ color.rgb *= color.a; // Premultiply alpha
248
+ color = clamp(color, 0.0, 1.0);
249
+ }
250
+
251
+
252
+ if(u_temperature != 0.0){
253
+ float temperature = u_temperature;
254
+ const float tint = 0.0;
255
+ vec4 source = color;
256
+
257
+ source.rgb /= max(source.a, EPSILON); // Revert premultiplied alpha
258
+
259
+ vec3 yiq = RGBtoYIQ * source.rgb;
260
+ yiq.b = clamp(yiq.b + tint*0.5226*0.1, -0.5226, 0.5226);
261
+ vec3 rgb = YIQtoRGB * yiq;
262
+
263
+ vec3 processed = mix(
264
+ (1.0 - 2.0 * (1.0 - rgb) * (1.0 - warmFilter)),
265
+ (2.0 * rgb * warmFilter),
266
+ vec3(rgb.r < 0.5, rgb.g < 0.5, rgb.b < 0.5)
267
+ );
268
+
269
+ color = vec4(mix(rgb, processed, temperature), source.a);
270
+
271
+ color.rgb *= color.a; // Premultiply alpha again
272
+
273
+ }
274
+
275
+ if (u_sharpness != 0.0){
276
+ float factor = mix(0.2, -1.0, float(u_sharpness > 0.0));
277
+ vec4 sharpenedColor = mix(0.2, 5.0, float(u_sharpness > 0.0)) * color;
278
+
279
+ sharpenedColor += factor * clamp(texture2D(u_image, v_texCoord + u_pixelDimension * vec2(-1.0, 0.0)), 0.0, 1.0);
280
+ sharpenedColor += factor * clamp(texture2D(u_image, v_texCoord + u_pixelDimension * vec2( 0.0, -1.0)), 0.0, 1.0);
281
+ sharpenedColor += factor * clamp(texture2D(u_image, v_texCoord + u_pixelDimension * vec2( 0.0, 1.0)), 0.0, 1.0);
282
+ sharpenedColor += factor * clamp(texture2D(u_image, v_texCoord + u_pixelDimension * vec2( 1.0, 0.0)), 0.0, 1.0);
283
+
284
+ color.rgb /= max(color.a, EPSILON); // unpremultiply
285
+ sharpenedColor.rgb /= max(sharpenedColor.a, EPSILON); // unpremultiply
286
+
287
+ sharpenedColor = clamp(sharpenedColor, 0.0, 1.0);
288
+
289
+ color = clamp(mix(color, sharpenedColor, abs(u_sharpness)), 0.0, 1.0);
290
+
291
+ color = vec4(color.rgb * color.a, color.a); // premultiply
292
+
293
+ }
294
+
295
+
296
+ gl_FragColor = color;
297
+ }
298
+
299
+ """
300
+
301
+ #
302
+
303
+ # Compile shaders
304
+ shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
305
+ OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER))
306
+
307
+ # VBO
308
+ v_b_o = glGenBuffers(1)
309
+ glBindBuffer(GL_ARRAY_BUFFER, v_b_o)
310
+ glBufferData(GL_ARRAY_BUFFER, quad.itemsize *
311
+ len(quad), quad, GL_STATIC_DRAW)
312
+
313
+ # EBO
314
+ e_b_o = glGenBuffers(1)
315
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, e_b_o)
316
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.itemsize *
317
+ len(indices), indices, GL_STATIC_DRAW)
318
+
319
+ # Configure positions of initial data
320
+ # Configure positions of initial data
321
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 *
322
+ sizeof(GLfloat), ctypes.c_void_p(0))
323
+ glEnableVertexAttribArray(0)
324
+
325
+ # Configure texture coordinates of initial data
326
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 *
327
+ sizeof(GLfloat), ctypes.c_void_p(8))
328
+ glEnableVertexAttribArray(1)
329
+
330
+ # Texture
331
+ texture = glGenTextures(1)
332
+ # Bind texture
333
+ glBindTexture(GL_TEXTURE_2D, texture)
334
+ # Texture wrapping params
335
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
336
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
337
+ # Texture filtering params
338
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
339
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
340
+
341
+ #
342
+
343
+ # Open image
344
+
345
+ #
346
+ # img_data = numpy.array(list(image.getdata()), numpy.uint8)
347
+ #
348
+ # flipped_image = image.transpose(Image.FLIP_TOP_BOTTOM)
349
+ # img_data = flipped_image.convert("RGBA").tobytes()
350
+ #
351
+ img_data = image.convert("RGBA").tobytes()
352
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width,
353
+ image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data)
354
+ # print(image.width, image.height)
355
+
356
+ #
357
+
358
+ # Create render buffer with size (image.width x image.height)
359
+ rb_obj = glGenRenderbuffers(1)
360
+ glBindRenderbuffer(GL_RENDERBUFFER, rb_obj)
361
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, image.width, image.height)
362
+
363
+ # Create frame buffer
364
+ fb_obj = glGenFramebuffers(1)
365
+ glBindFramebuffer(GL_FRAMEBUFFER, fb_obj)
366
+ glFramebufferRenderbuffer(
367
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb_obj)
368
+
369
+ # Check frame buffer (that simple buffer should not be an issue)
370
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
371
+ if status != GL_FRAMEBUFFER_COMPLETE:
372
+ print("incomplete framebuffer object")
373
+
374
+ #
375
+
376
+ # Install program
377
+ glUseProgram(shader)
378
+
379
+ set_options(exposure, saturation, contrast, brightness, gamma, shadows, highlights, whites, blacks,
380
+ clarity, temperature, sharpness, shader, image.width, image.height)
381
+
382
+ # Bind framebuffer and set viewport size
383
+ glBindFramebuffer(GL_FRAMEBUFFER, fb_obj)
384
+ glViewport(0, 0, image.width, image.height)
385
+
386
+ # Draw the quad which covers the entire viewport
387
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
388
+
389
+ #
390
+
391
+ # PNG
392
+ # Read the data and create the image
393
+ image_buffer = glReadPixels(
394
+ 0, 0, image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE)
395
+ image_out = numpy.frombuffer(image_buffer, dtype=numpy.uint8)
396
+ image_out = image_out.reshape(image.height, image.width, 4)
397
+ glfw.terminate()
398
+ img = Image.fromarray(image_out, 'RGB')
399
+
400
+
401
+ buffered = BytesIO()
402
+ img.save(buffered, format="PNG")
403
+ img_str = base64.b64encode(buffered.getvalue())
404
+
405
+ return img_str
406
+
407
+
408
+
409
+ if __name__ == "__main__":
410
+ image = Image.open("/Users/planningo/Downloads/download.jpeg")
411
+ image_enhance(image, exposure=0, saturation=0, contrast=0, brightness=0, gamma=1, shadows=0,
412
+ highlights=0, blacks=0, whites=0, clarity=0, temperature=-1, sharpness=1)
requirments.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ glfw
2
+ PyOpenGL
3
+ numpy
4
+ Pillow
5
+ fastapi==0.104.1
6
+ uvicorn
7
+ pydantic
utils/ColorMatrix.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+
4
+ class ColorMatrix:
5
+ def __init__(self, a=1, b=0, c=0, d=0, e=0, f=0, g=1, h=0, i=0, j=0, k=0, l=0, m=1, n=0, o=0, p=0, q=0, r=0, s=1, t=0):
6
+ self.a = a
7
+ self.b = b
8
+ self.c = c
9
+ self.d = d
10
+ self.e = e
11
+ self.f = f
12
+ self.g = g
13
+ self.h = h
14
+ self.i = i
15
+ self.j = j
16
+ self.k = k
17
+ self.l = l
18
+ self.m = m
19
+ self.n = n
20
+ self.o = o
21
+ self.p = p
22
+ self.q = q
23
+ self.r = r
24
+ self.s = s
25
+ self.t = t
26
+
27
+ self.matrix = np.array([
28
+ [a, b, c, d, e],
29
+ [f, g, h, i, j],
30
+ [k, l, m, n, o],
31
+ [p, q, r, s, t],
32
+ [0, 0, 0, 0, 1]
33
+ ])
34
+
35
+ def apply(self, color):
36
+ r = self.a * color.r + self.b * color.g + \
37
+ self.c * color.b + self.d * color.a + self.e
38
+ g = self.f * color.r + self.g * color.g + \
39
+ self.h * color.b + self.i * color.a + self.j
40
+ b = self.k * color.r + self.l * color.g + \
41
+ self.m * color.b + self.n * color.a + self.o
42
+ a = self.p * color.r + self.q * color.g + \
43
+ self.r * color.b + self.s * color.a + self.t
44
+ return Color(r, g, b, a)
45
+
46
+ def reset(self):
47
+ self.a = 1
48
+ self.b = 0
49
+ self.c = 0
50
+ self.d = 0
51
+ self.e = 0
52
+ self.f = 0
53
+ self.g = 1
54
+ self.h = 0
55
+ self.i = 0
56
+ self.j = 0
57
+ self.k = 0
58
+ self.l = 0
59
+ self.m = 1
60
+ self.n = 0
61
+ self.o = 0
62
+ self.p = 0
63
+ self.q = 0
64
+ self.r = 0
65
+ self.s = 1
66
+ self.t = 0
67
+ self.matrix = np.array([
68
+ [1, 0, 0, 0, 0],
69
+ [0, 1, 0, 0, 0],
70
+ [0, 0, 1, 0, 0],
71
+ [0, 0, 0, 1, 0],
72
+ [0, 0, 0, 0, 1]
73
+ ])
74
+
75
+ def multiply(self, other):
76
+ E = other
77
+ R = self
78
+ t = E.a * R.a + E.b * R.f + E.c * R.k + E.d * R.p
79
+ i = E.a * R.b + E.b * R.g + E.c * R.l + E.d * R.q
80
+ o = E.a * R.c + E.b * R.h + E.c * R.m + E.d * R.r
81
+ n = E.a * R.d + E.b * R.i + E.c * R.n + E.d * R.s
82
+ a = E.f * R.a + E.g * R.f + E.h * R.k + E.i * R.p
83
+ l = E.f * R.b + E.g * R.g + E.h * R.l + E.i * R.q
84
+ u = E.f * R.c + E.g * R.h + E.h * R.m + E.i * R.r
85
+ c = E.f * R.d + E.g * R.i + E.h * R.n + E.i * R.s
86
+ m = E.k * R.a + E.l * R.f + E.m * R.k + E.n * R.p
87
+ h = E.k * R.b + E.l * R.g + E.m * R.l + E.n * R.q
88
+ f = E.k * R.c + E.l * R.h + E.m * R.m + E.n * R.r
89
+ b = E.k * R.d + E.l * R.i + E.m * R.n + E.n * R.s
90
+ y = E.p * R.a + E.q * R.f + E.r * R.k + E.s * R.p
91
+ T = E.p * R.b + E.q * R.g + E.r * R.l + E.s * R.q
92
+ w = E.p * R.c + E.q * R.h + E.r * R.m + E.s * R.r
93
+ k = E.p * R.d + E.q * R.i + E.r * R.n + E.s * R.s
94
+ s = E.a * R.e + E.b * R.j + E.c * R.o + E.d * R.t + E.e
95
+ d = E.f * R.e + E.g * R.j + E.h * R.o + E.i * R.t + E.j
96
+ _ = E.k * R.e + E.l * R.j + E.m * R.o + E.n * R.t + E.o
97
+ F = E.p * R.e + E.q * R.j + E.r * R.o + E.s * R.t + E.t
98
+ self.a = t
99
+ self.b = i
100
+ self.c = o
101
+ self.d = n
102
+ self.e = s
103
+ self.f = a
104
+ self.g = l
105
+ self.h = u
106
+ self.i = c
107
+ self.j = d
108
+ self.k = m
109
+ self.l = h
110
+ self.m = f
111
+ self.n = b
112
+ self.o = _
113
+ self.p = y
114
+ self.q = T
115
+ self.r = w
116
+ self.s = k
117
+ self.t = F
118
+ return self
119
+
120
+ def clone(self):
121
+ newMatrix = ColorMatrix()
122
+ newMatrix.setMatrix(
123
+ self.a,
124
+ self.b,
125
+ self.c,
126
+ self.d,
127
+ self.e,
128
+ self.f,
129
+ self.g,
130
+ self.h,
131
+ self.i,
132
+ self.j,
133
+ self.k,
134
+ self.l,
135
+ self.m,
136
+ self.n,
137
+ self.o,
138
+ self.p,
139
+ self.q,
140
+ self.r,
141
+ self.s,
142
+ self.t
143
+ )
144
+ return newMatrix
145
+
146
+ def equals(self, other):
147
+ return np.array_equal(self.matrix, other.matrix)
148
+
149
+ def get_offsets(self):
150
+ return [self.e, self.j, self.o, self.t]
151
+
152
+ def __str__(self):
153
+ return str(self.matrix)
154
+
155
+ def to_array(self):
156
+ components = [
157
+ self.a,
158
+ self.b,
159
+ self.c,
160
+ self.d,
161
+ self.e,
162
+ self.f,
163
+ self.g,
164
+ self.h,
165
+ self.i,
166
+ self.j,
167
+ self.k,
168
+ self.l,
169
+ self.m,
170
+ self.n,
171
+ self.o,
172
+ self.p,
173
+ self.q,
174
+ self.r,
175
+ self.s,
176
+ self.t,
177
+ ]
178
+ return components
179
+
180
+ @staticmethod
181
+ def create_brightness_matrix(value):
182
+ matrix = ColorMatrix()
183
+ matrix.e = value
184
+ matrix.j = value
185
+ matrix.o = value
186
+ return matrix
187
+
188
+ @staticmethod
189
+ def create_contrast_matrix(value):
190
+ matrix = ColorMatrix()
191
+ i = (1 - value) / 2
192
+ matrix.a = matrix.g = matrix.m = value
193
+ matrix.e = matrix.j = matrix.o = i
194
+ return matrix
195
+
196
+ @staticmethod
197
+ def create_saturation_matrix(value=1):
198
+ matrix = ColorMatrix()
199
+ i = 1 - value
200
+ o = 0.213 * i
201
+ n = 0.715 * i
202
+ s = 0.072 * i
203
+ matrix.a = o + value
204
+ matrix.b = n
205
+ matrix.c = s
206
+ matrix.f = o
207
+ matrix.g = n + value
208
+ matrix.h = s
209
+ matrix.k = o
210
+ matrix.l = n
211
+ matrix.m = s + value
212
+ return matrix
213
+
214
+ @staticmethod
215
+ def create_exposure_matrix(value=0):
216
+ exposure = 2**value
217
+ return ColorMatrix(exposure, 0, 0, 0, 0, 0, exposure, 0, 0, 0, 0, 0, exposure, 0, 0, 0, 0, 0, 1, 0)
218
+
219
+ @staticmethod
220
+ def createLinearMatrix(value=1, offset=0):
221
+ matrix = ColorMatrix()
222
+ matrix.a = matrix.g = matrix.m = value
223
+ matrix.e = matrix.j = matrix.o = offset
224
+ return matrix
225
+
226
+ # Define the Color class if not already defined
227
+
228
+
229
+ class Color:
230
+ def __init__(self, r, g, b, a):
231
+ self.r = r
232
+ self.g = g
233
+ self.b = b
234
+ self.a = a
utils/dto.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class ToneUpData(BaseModel):
5
+ image_id : str
6
+ exposure : float
7
+ saturation : float
8
+ contrast : float
9
+ brightness : float
10
+ gamma : float
11
+ shadows : float
12
+ highlights : float
13
+ whites : float
14
+ blacks : float
15
+ clarity : float
16
+ temperature : float
17
+ sharpness : float
utils/s3.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import boto3
2
+ from PIL import Image
3
+ from io import BytesIO
4
+
5
+ BUCKET_NAME = 'planningo-public'
6
+ DIR_NAME = 'photio/'
7
+
8
+ s3 = boto3.client('s3', aws_access_key_id='AKIAZK42H2NEXBFJWUTD',
9
+ aws_secret_access_key='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')
10
+
11
+ def get_image_from_s3(image_id):
12
+ base_image_data = s3.get_object(
13
+ Bucket=BUCKET_NAME, Key=f'{DIR_NAME}{image_id}.png')['Body'].read()
14
+ return Image.open(BytesIO(base_image_data))
utils/settings.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from OpenGL.GL import *
3
+
4
+ from ColorMatrix import ColorMatrix
5
+
6
+
7
+ def set_uniform(uniform_name, value, type, shader_program):
8
+ uniform_location = glGetUniformLocation(
9
+ shader_program, f"u_{uniform_name}")
10
+ if type == '1f':
11
+ glUniform1f(uniform_location, value)
12
+ elif type == '2fv':
13
+ glUniform2fv(uniform_location, 1, (GLfloat * len(value))(*value))
14
+ elif type == '4fv':
15
+ glUniform4fv(uniform_location, 1, (GLfloat * len(value))(*value))
16
+ elif type == 'mat4':
17
+ glUniformMatrix4fv(uniform_location, 1, GL_FALSE,
18
+ (GLfloat * len(value))(*value))
19
+
20
+
21
+ def set_color_matrix_uniform(color_matrix, val_name, shader_program):
22
+ t = color_matrix
23
+
24
+ set_uniform(
25
+ f"{val_name}Matrix",
26
+ [t.a, t.b, t.c, t.d, t.f, t.g, t.h, t.i,
27
+ t.k, t.l, t.m, t.n, t.p, t.q, t.r, t.s],
28
+ 'mat4',
29
+ shader_program
30
+ )
31
+
32
+ set_uniform(f"{val_name}Matrix_vec", [
33
+ t.e, t.j, t.o, t.t], '4fv', shader_program)
34
+ set_uniform(f"{val_name}Offset", color_matrix.get_offsets(),
35
+ '4fv', shader_program)
36
+
37
+
38
+ def set_options(exposure, saturation, contrast, brightness, gamma, shadows, highlights, whites, blacks,
39
+ clarity, temperature, sharpness, shader_program, canvas_width, canvas_height):
40
+ if not shader_program:
41
+ print('Shader program not initialized')
42
+ return
43
+
44
+ set_uniform('gamma', gamma, '1f', shader_program)
45
+ set_uniform('shadows', shadows, '1f', shader_program)
46
+ set_uniform('highlights', highlights, '1f', shader_program)
47
+ set_uniform('whites', whites, '1f', shader_program)
48
+ set_uniform('blacks', blacks, '1f', shader_program)
49
+ set_uniform('clarity', clarity, '1f', shader_program)
50
+ set_uniform('temperature', temperature, '1f', shader_program)
51
+ set_uniform('sharpness', sharpness, '1f', shader_program)
52
+
53
+ color_matrix = ColorMatrix()
54
+ color_matrix.multiply(ColorMatrix.create_exposure_matrix(exposure))
55
+ print(vars(color_matrix))
56
+ color_matrix.multiply(ColorMatrix.create_saturation_matrix(saturation + 1))
57
+ print(vars(color_matrix))
58
+ if contrast > 0:
59
+ contrast = contrast * 2
60
+
61
+ color_matrix.multiply(ColorMatrix.create_contrast_matrix(
62
+ contrast + 1))
63
+ print(vars(color_matrix))
64
+ color_matrix.multiply(ColorMatrix.create_brightness_matrix(brightness))
65
+ print(vars(color_matrix))
66
+ set_color_matrix_uniform(color_matrix, 'color', shader_program)
67
+
68
+ clarity_matrix = ColorMatrix()
69
+ clarity_matrix.multiply(
70
+ ColorMatrix.create_saturation_matrix(-0.3 * clarity + 1))
71
+ clarity_matrix.multiply(
72
+ ColorMatrix.create_contrast_matrix(0.1 * clarity + 1))
73
+ set_color_matrix_uniform(clarity_matrix, 'clarity', shader_program)
74
+ print('pixelDimension', [
75
+ 1 / canvas_width, 1 / canvas_height])
76
+ set_uniform('pixelDimension', [
77
+ 1 / canvas_width, 1 / canvas_height], '2fv', shader_program)