HuguesdeF commited on
Commit
78c8c21
1 Parent(s): 00bf95c

added files

Browse files
Corriger.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from code.functions import pipeline_svg
3
+ from PIL import Image
4
+ import cv2
5
+ import numpy as np
6
+ from io import BytesIO
7
+ import copy
8
+
9
+ inch_value = 2.54
10
+
11
+ logo = Image.open("seguinmoreau.png")
12
+ st.set_page_config(
13
+ page_title="Moulinette Logos",
14
+ page_icon=logo,
15
+ layout="wide",
16
+ initial_sidebar_state="expanded"
17
+ )
18
+
19
+ st.markdown(
20
+ """
21
+ # Boîte à Outils de correction de logos :wrench:
22
+
23
+ Bienvenue dans la boîte à outils de correction de logos de Seguin Moreau.
24
+
25
+ ### :hammer: Les outils
26
+ Dans cette boîte à outils, vous trouverez:
27
+ * Un outil de Correction automatique de logo (enlever les petits défauts, lissage, vectorisation, grossissement des traits trop fins.
28
+ * Un outil de Vectorisation (image en pixels => image vectorisée => image en pixels).
29
+
30
+ ### :bulb: Mode d'emploi
31
+ * Cliquer sur 'Browse files'
32
+ * Sélectionner un logo
33
+ * La correction est automatique. Si la correction ne vous convient pas, il est possible de régler les paramètres en cliquant sur 'Paramétrage' à droite de l'image.
34
+ * Les deux paramètres permettent de corriger les défauts liés à la présence de gris sur le logo ou la 'pixélisation' du logo trop importante.
35
+
36
+ """
37
+ )
38
+
39
+ logo = Image.open('seguinmoreau.png')
40
+ st.image(logo, width=100)
41
+
42
+ uploaded_files = st.file_uploader("Choisir un logo", accept_multiple_files=True)
43
+
44
+ image_width = 500
45
+ size_value = st.slider("Largeur de trait minimum", min_value=1, max_value=21, value=7, step=2)
46
+
47
+ size_value = (size_value - 1) // 2
48
+
49
+ #kernel_type_str = st.selectbox("Kernel type", ["Ellipse", "Rectangle", "Cross"])
50
+ kernel_type_str = "Ellipse"
51
+ dict_kernel_type = {"Ellipse": cv2.MORPH_ELLIPSE, "Rectangle": cv2.MORPH_RECT, "Cross": cv2.MORPH_CROSS}
52
+ kernel_type = dict_kernel_type[kernel_type_str]
53
+
54
+ for uploaded_file in uploaded_files:
55
+ col1, col2, col3 = st.columns([1, 1, 1])
56
+ col3.markdown("---")
57
+
58
+ image = Image.open(uploaded_file).convert('L')
59
+ image_input = np.array(image)
60
+ image = copy.deepcopy(image_input)
61
+ col1.image(image_input/255.0, caption="Image d'entrée", use_column_width='auto')
62
+
63
+ with col3:
64
+ with st.expander(":gear: Paramétrage"):
65
+ st.write("Si l'image contient du gris, faire varier le seuil ci-dessous:")
66
+ threshold = st.slider("Seuil pour convertir l'image en noir&blanc.", min_value=0, max_value=255, value=0,
67
+ step=1, key=f"{uploaded_file}_slider_threshold")
68
+ st.write("Si l'image est pixelisée, ou contient trop de détails, "
69
+ "augmenter la valeur ci-dessous:")
70
+ blur_value = st.slider("Seuil pour lisser l'image", min_value=1, max_value=11, value=1, step=2,
71
+ key=f"{uploaded_file}_slider_gaussian_sigma")
72
+ st.write("Si l'image contient des traits très fin (de l'odre du pixel),"
73
+ " augmenter le seuil ci-dessous, de 1 par 1:")
74
+ dilate_lines_value = st.slider("Dilatation de l'image d'origine: (en pixels)", min_value=0, max_value=5, value=0, step=1,key=f"{uploaded_file}_slider_dilation_image")
75
+
76
+ st.write("Taille d'exportation d'image:")
77
+
78
+ dpi_value = st.number_input("Valeur dpi:", key=f"{uploaded_file}_number_dpi_value", value=200)
79
+ side_width_value = st.number_input("Taille max de côté cible (cm):", key=f"{uploaded_file}_number_target_value", value=20)
80
+ new_largest_side_value = int(side_width_value / inch_value * dpi_value)
81
+
82
+ h, w, *_ = image.shape
83
+
84
+ # Resize image
85
+ ratio = w / h
86
+ if ratio > 1:
87
+ width = new_largest_side_value
88
+ height = int(new_largest_side_value / ratio)
89
+ else:
90
+ height = new_largest_side_value
91
+ width = int(ratio * new_largest_side_value)
92
+
93
+ target_width_value = st.number_input("Largeur cible (cm):", key=f"{uploaded_file}_number_width_value", value=0)
94
+ target_height_value = st.number_input("Hauteur cible (cm):", key=f"{uploaded_file}_number_height_value", value=0)
95
+
96
+ if target_width_value > 0 and target_height_value == 0:
97
+ width = int(target_width_value / inch_value * dpi_value)
98
+ height = int(width / ratio)
99
+ elif target_height_value > 0 and target_width_value == 0:
100
+ height = int(target_height_value / inch_value * dpi_value)
101
+ width = int(height * ratio)
102
+ elif target_height_value > 0 and target_width_value > 0:
103
+ st.warning("Vous ne pouvez pas modifier la largeur et la hauteur simultanément.")
104
+
105
+ if threshold > 0:
106
+ image = (image > threshold)*255
107
+ image = image.astype('uint8')
108
+
109
+ if blur_value > 0:
110
+ image = cv2.GaussianBlur(image, (blur_value, blur_value), blur_value - 1)
111
+
112
+ # Process image cv32f ==> cv32f
113
+ img_final = pipeline_svg(image, size_value=size_value, level=1, threshold=threshold, kernel_type=kernel_type, dilate_lines_value=dilate_lines_value)
114
+
115
+ col2.image(img_final, caption="Image corrigée", use_column_width='auto')
116
+
117
+ # Check for grayscale
118
+ tolerance = 10
119
+ ratio_of_gray_pixels = int(np.sum((tolerance < image)* (image < 255 - tolerance))/np.size(image)*100)
120
+ if ratio_of_gray_pixels > 1:
121
+ col3.warning(f":warning: Le nombre de pixels gris est élevé: {ratio_of_gray_pixels} % > 1%")
122
+
123
+ # Check reconstruction fidelity
124
+ distance = np.mean((np.array(image) - img_final)**2)
125
+ if distance > 10:
126
+ col3.warning(f":warning: Le logo est peut-être trop dégradé (MSE={distance:.2f} > 10).\nVérifier visuellement.")
127
+
128
+
129
+
130
+ dim = (width, height)
131
+ # resize image
132
+ resized_img_final = cv2.resize(img_final, dim, interpolation=cv2.INTER_AREA)
133
+ resized_image_input = cv2.resize(image_input, dim, interpolation=cv2.INTER_AREA)
134
+
135
+ buf = BytesIO()
136
+ img_stacked = np.hstack((resized_image_input, resized_img_final))
137
+ img_final = Image.fromarray(img_stacked).convert("L")
138
+ img_final.save(buf, format="PNG")
139
+ byte_im= buf.getvalue()
140
+
141
+ btn = col3.download_button(
142
+ label=":inbox_tray: Télécharger l'image",
143
+ data=byte_im,
144
+ file_name=f"corrected_{uploaded_file.name}",
145
+ mime="image/png"
146
+ )
147
+
148
+
149
+
150
+
151
+
152
+
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ EXPOSE 7860
4
+
5
+ RUN apt-get update && apt-get install -y \
6
+ build-essential \
7
+ software-properties-common \
8
+ git \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ WORKDIR /moulinette
12
+ COPY . .
13
+ RUN echo $(ls -1 .. )
14
+
15
+ RUN pip3 install -r requirements.txt
16
+
17
+ ENTRYPOINT ["streamlit", "run", "Corriger.py", "--server.port=7860"]
README.md CHANGED
@@ -1,10 +1,27 @@
1
- ---
2
- title: Moulinette
3
- emoji: 🔥
4
- colorFrom: indigo
5
- colorTo: red
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # moulinette
2
+ moulinette
3
+
4
+
5
+ #Command line svg to png
6
+ inkscape -h 1468 -w 1576 imagea10.svg -o output.png --export-background-opacity=255
7
+
8
+ # command line png to svg
9
+ pip install potrace-cli
10
+ potracer ../SMoreau/images/flower.png -b svg -o image.png
11
+
12
+ # Or image j
13
+ convert image10.svg -colorspace Gray output.png
14
+
15
+ # Put to aws
16
+
17
+ Follow
18
+ https://itnext.io/run-your-containers-on-aws-fargate-c2d4f6a47fda
19
+ Permission denied ==> use sudo.
20
+
21
+ 1/ build docker
22
+ 2/ push docker image on aws ECR (elastic container registery)
23
+
24
+ Error in building the docker use
25
+ https://stackoverflow.com/questions/72564830/python3-minimal-error-during-pip-installation-in-docker-build-permissionerro
26
+
27
+ Il faut absolument ne pas être sudo pour docker !
code/__init__.py ADDED
File without changes
code/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (135 Bytes). View file
 
code/__pycache__/functions.cpython-39.pyc ADDED
Binary file (4.52 kB). View file
 
code/functions.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ import os
5
+ import cairosvg
6
+ from potrace import POTRACE_CORNER, Path, Bitmap
7
+ import io
8
+ from PIL import Image, ImageStat
9
+
10
+ import streamlit
11
+ from PIL import Image
12
+
13
+ @streamlit.cache_data
14
+ def pipeline_svg(image_input, size_value, level=3, streamlit=False, threshold=0, kernel_type=cv2.MORPH_ELLIPSE, dilate_lines_value=0):
15
+ """
16
+ uint8 ==> uint8
17
+
18
+ Args:
19
+ streamlit:
20
+ size_value:
21
+ image_input:
22
+
23
+ Returns:
24
+
25
+ """
26
+
27
+ # Process image
28
+ image_processed = process_svg(image_input, size_value=size_value, streamlit=streamlit, kernel_type=kernel_type, dilate_lines_value=dilate_lines_value)
29
+
30
+ return image_processed
31
+
32
+ def process_svg(img, size_value=12, level=1, streamlit=False, kernel_type=cv2.MORPH_ELLIPSE, dilate_lines_value=0):
33
+
34
+ image_path = "input_image.png"
35
+ img = img.astype('uint8')
36
+
37
+ # Lines very small
38
+ if dilate_lines_value > 0:
39
+ size = dilate_lines_value + 1 # No sens to dilate by one pixel (doesn't do anything).
40
+ kernel = get_kernel_ellipse(size, kernel_type=kernel_type)
41
+ img = cv2.erode(img, kernel, iterations=1)
42
+
43
+ cv2.imwrite(image_path, img)
44
+ img_array = convert_to_svg_and_back(img)
45
+
46
+ img_array = binarise(img_array)
47
+ img_bin = (255 - img_array)
48
+ img_bin = img_bin.astype('uint8')
49
+ image_already_added = np.zeros_like(img_bin)
50
+
51
+ target_min_size = max(1, size_value)
52
+
53
+ image_final = img_bin
54
+ for i in range(target_min_size+1):
55
+ size = 2 * i + 1
56
+ kernel = get_kernel_ellipse(size, kernel_type=kernel_type)
57
+
58
+ erosion = cv2.erode((img_bin - image_already_added), kernel, iterations=1)
59
+ dilation = cv2.dilate(erosion, kernel, iterations=1)
60
+
61
+ image_petits_objets = (img_bin - dilation)
62
+ image_petits_objets = remove_solo_pixels(image_petits_objets, kernel_size=3)
63
+
64
+ size = 2 * (target_min_size - i) + 1
65
+ kernel = get_kernel_ellipse(size, kernel_type=kernel_type)
66
+ dilate_image_petits_objets = cv2.dilate(image_petits_objets, kernel, iterations=1)
67
+
68
+ image_already_added = (image_already_added + image_petits_objets)
69
+
70
+ if i > level:
71
+ image_final = (image_final + dilate_image_petits_objets)
72
+
73
+ cv2.imwrite("image_finale.png", (255 - image_final))
74
+ image = convert_to_svg_and_back((255 - image_final))
75
+ return image
76
+ def get_kernel_ellipse(size, kernel_type=cv2.MORPH_ELLIPSE):
77
+ list_coords = [size, size]
78
+ return cv2.getStructuringElement(kernel_type, (list_coords[0], list_coords[1]),
79
+ (int((list_coords[0] - 1) / 2), int((list_coords[1] - 1) / 2)))
80
+
81
+
82
+ def binarise(img):
83
+ img = img > 200
84
+ img = img * 255
85
+ img = img.astype('uint8')
86
+ return img
87
+
88
+
89
+ def imshow(title, image, vmin=0, vmax=1):
90
+ plt.figure()
91
+ plt.title(title)
92
+ plt.imshow(image * 255, vmin=vmin * 255, vmax=vmax * 255, cmap='gray')
93
+
94
+
95
+ def remove_solo_pixels(image, kernel_size=3):
96
+ kernel = get_kernel_ellipse(kernel_size)
97
+
98
+ erosion = cv2.erode(image, kernel, iterations=1)
99
+ dilation = cv2.dilate(erosion, kernel, iterations=1)
100
+
101
+ dilation = dilation.astype('uint8')
102
+ return dilation
103
+
104
+ def convert_to_svg_and_back(image_array) -> np.array:
105
+ image_pil = Image.fromarray(image_array)
106
+
107
+ bm = Bitmap(image_pil, blacklevel=0.5)
108
+
109
+ plist = bm.trace(
110
+ turdsize=2,
111
+ turnpolicy=4,
112
+ alphamax=1,
113
+ opticurve= False,
114
+ opttolerance=0.2)
115
+
116
+ image = backend_svg_no_file(image_pil, plist)
117
+
118
+ return np.array(image)
119
+
120
+ def backend_svg_no_file(image, path: Path):
121
+ output = f'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{image.width}" height="{image.height}" viewBox="0 0 {image.width} {image.height}">'
122
+
123
+ parts = []
124
+ for curve in path:
125
+ fs = curve.start_point
126
+ parts.append("M%f,%f" % (fs.x, fs.y))
127
+ for segment in curve.segments:
128
+ if segment.is_corner:
129
+ a = segment.c
130
+ parts.append("L%f,%f" % (a.x, a.y))
131
+ b = segment.end_point
132
+ parts.append("L%f,%f" % (b.x, b.y))
133
+ else:
134
+ a = segment.c1
135
+ b = segment.c2
136
+ c = segment.end_point
137
+ parts.append("C%f,%f %f,%f %f,%f" % (a.x, a.y, b.x, b.y, c.x, c.y))
138
+ parts.append("z")
139
+ output += f'<path stroke="none" fill="#000000" fill-rule="evenodd" d="{"".join(parts)}"/>'
140
+
141
+ output += "</svg>"
142
+ # From svg to png (bytes)
143
+ image_data = cairosvg.surface.PNGSurface.convert(output)
144
+ image = Image.open(io.BytesIO(image_data)).split()[-1]
145
+ return image
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ matplotlib==3.4.1
2
+ numpy==1.20.2
3
+ opencv_python_headless==4.5.5.64
4
+ Pillow==9.4.0
5
+ scipy==1.6.2
6
+ streamlit==1.20.0
7
+ potracer==0.0.4
8
+ cairosvg==2.7.0
seguinmoreau.png ADDED