Spaces:
Build error
Build error
added files
Browse files- Corriger.py +175 -0
- Dockerfile +19 -0
- README.md +3 -3
- code/__init__.py +0 -0
- code/__pycache__/__init__.cpython-39.pyc +0 -0
- code/__pycache__/functions.cpython-39.pyc +0 -0
- code/functions.py +145 -0
- packages.txt +2 -0
- requirements.txt +9 -0
- seguinmoreau.png +0 -0
- users.yaml +21 -0
Corriger.py
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import streamlit_authenticator as stauth
|
3 |
+
from code.functions import pipeline_svg
|
4 |
+
from PIL import Image
|
5 |
+
import cv2
|
6 |
+
import numpy as np
|
7 |
+
from io import BytesIO
|
8 |
+
import copy
|
9 |
+
import yaml
|
10 |
+
from yaml.loader import SafeLoader
|
11 |
+
|
12 |
+
logo = Image.open("seguinmoreau.png")
|
13 |
+
st.set_page_config(
|
14 |
+
page_title="Moulinette Logos",
|
15 |
+
page_icon=logo,
|
16 |
+
layout="wide",
|
17 |
+
initial_sidebar_state="expanded"
|
18 |
+
)
|
19 |
+
|
20 |
+
# Authentication
|
21 |
+
|
22 |
+
with open('users.yaml') as file:
|
23 |
+
config = yaml.load(file, Loader=SafeLoader)
|
24 |
+
|
25 |
+
authenticator = stauth.Authenticate(
|
26 |
+
config['credentials'],
|
27 |
+
config['cookie']['name'],
|
28 |
+
config['cookie']['key'],
|
29 |
+
config['cookie']['expiry_days'],
|
30 |
+
config['preauthorized']
|
31 |
+
)
|
32 |
+
|
33 |
+
name, authentication_status, username = authenticator.login('Login', 'main')
|
34 |
+
|
35 |
+
if not authentication_status:
|
36 |
+
st.error("Nom d'utilisateur ou mot de passe incorrect")
|
37 |
+
elif authentication_status is None:
|
38 |
+
st.warning("Rentrer nom d'utilisateur et mot de passe")
|
39 |
+
elif authentication_status:
|
40 |
+
authenticator.logout('Logout', 'main')
|
41 |
+
# ------------------------------
|
42 |
+
|
43 |
+
inch_value = 2.54
|
44 |
+
|
45 |
+
logo = Image.open('seguinmoreau.png')
|
46 |
+
st.image(logo, width=200)
|
47 |
+
st.markdown(
|
48 |
+
"""
|
49 |
+
# Boîte à Outils de correction de logos :wrench:
|
50 |
+
|
51 |
+
Bienvenue dans la boîte à outils de correction de logos de Seguin Moreau.
|
52 |
+
|
53 |
+
### :hammer: Les outils
|
54 |
+
Dans cette boîte à outils, vous trouverez:
|
55 |
+
* Un outil de Correction automatique de logo (enlever les petits défauts, lissage, vectorisation, grossissement des traits trop fins).
|
56 |
+
|
57 |
+
### :bulb: Mode d'emploi
|
58 |
+
* Cliquer sur 'Browse files'
|
59 |
+
* Sélectionner un logo
|
60 |
+
* 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.
|
61 |
+
* 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.
|
62 |
+
|
63 |
+
"""
|
64 |
+
)
|
65 |
+
|
66 |
+
uploaded_files = st.file_uploader("Choisir un logo", accept_multiple_files=True)
|
67 |
+
|
68 |
+
image_width = 500
|
69 |
+
size_value = st.slider("Largeur de trait minimum", min_value=1, max_value=21, value=7, step=2)
|
70 |
+
|
71 |
+
size_value = (size_value - 1) // 2
|
72 |
+
|
73 |
+
# kernel_type_str = st.selectbox("Kernel type", ["Ellipse", "Rectangle", "Cross"])
|
74 |
+
kernel_type_str = "Ellipse"
|
75 |
+
dict_kernel_type = {"Ellipse": cv2.MORPH_ELLIPSE, "Rectangle": cv2.MORPH_RECT, "Cross": cv2.MORPH_CROSS}
|
76 |
+
kernel_type = dict_kernel_type[kernel_type_str]
|
77 |
+
|
78 |
+
for uploaded_file in uploaded_files:
|
79 |
+
col1, col2, col3 = st.columns([1, 1, 1])
|
80 |
+
col3.markdown("---")
|
81 |
+
|
82 |
+
image = Image.open(uploaded_file).convert('L')
|
83 |
+
image_input = np.array(image)
|
84 |
+
image = copy.deepcopy(image_input)
|
85 |
+
col1.image(image_input / 255.0, caption="Image d'entrée", use_column_width='auto')
|
86 |
+
|
87 |
+
with col3:
|
88 |
+
with st.expander(":gear: Paramétrage"):
|
89 |
+
st.write("Si l'image contient du gris, faire varier le seuil ci-dessous:")
|
90 |
+
threshold = st.slider("Seuil pour convertir l'image en noir&blanc.", min_value=0, max_value=255,
|
91 |
+
value=0,
|
92 |
+
step=1, key=f"{uploaded_file}_slider_threshold")
|
93 |
+
st.write("Si l'image est pixelisée, ou contient trop de détails, "
|
94 |
+
"augmenter la valeur ci-dessous:")
|
95 |
+
blur_value = st.slider("Seuil pour lisser l'image", min_value=1, max_value=11, value=1, step=2,
|
96 |
+
key=f"{uploaded_file}_slider_gaussian_sigma")
|
97 |
+
st.write("Si l'image contient des traits très fin (de l'odre du pixel),"
|
98 |
+
" augmenter le seuil ci-dessous, de 1 par 1:")
|
99 |
+
dilate_lines_value = st.slider("Dilatation de l'image d'origine: (en pixels)", min_value=0, max_value=5,
|
100 |
+
value=0, step=1, key=f"{uploaded_file}_slider_dilation_image")
|
101 |
+
|
102 |
+
st.write("Taille d'exportation d'image:")
|
103 |
+
|
104 |
+
dpi_value = st.number_input("Valeur dpi:", key=f"{uploaded_file}_number_dpi_value", value=200)
|
105 |
+
side_width_value = st.number_input("Taille max de côté cible (cm):",
|
106 |
+
key=f"{uploaded_file}_number_target_value", value=20)
|
107 |
+
new_largest_side_value = int(side_width_value / inch_value * dpi_value)
|
108 |
+
|
109 |
+
h, w, *_ = image.shape
|
110 |
+
|
111 |
+
# Resize image
|
112 |
+
ratio = w / h
|
113 |
+
if ratio > 1:
|
114 |
+
width = new_largest_side_value
|
115 |
+
height = int(new_largest_side_value / ratio)
|
116 |
+
else:
|
117 |
+
height = new_largest_side_value
|
118 |
+
width = int(ratio * new_largest_side_value)
|
119 |
+
|
120 |
+
target_width_value = st.number_input("Largeur cible (cm):", key=f"{uploaded_file}_number_width_value",
|
121 |
+
value=0)
|
122 |
+
target_height_value = st.number_input("Hauteur cible (cm):", key=f"{uploaded_file}_number_height_value",
|
123 |
+
value=0)
|
124 |
+
|
125 |
+
if target_width_value > 0 and target_height_value == 0:
|
126 |
+
width = int(target_width_value / inch_value * dpi_value)
|
127 |
+
height = int(width / ratio)
|
128 |
+
elif target_height_value > 0 and target_width_value == 0:
|
129 |
+
height = int(target_height_value / inch_value * dpi_value)
|
130 |
+
width = int(height * ratio)
|
131 |
+
elif target_height_value > 0 and target_width_value > 0:
|
132 |
+
st.warning("Vous ne pouvez pas modifier la largeur et la hauteur simultanément.")
|
133 |
+
|
134 |
+
if threshold > 0:
|
135 |
+
image = (image > threshold) * 255
|
136 |
+
image = image.astype('uint8')
|
137 |
+
|
138 |
+
if blur_value > 0:
|
139 |
+
image = cv2.GaussianBlur(image, (blur_value, blur_value), blur_value - 1)
|
140 |
+
|
141 |
+
# Process image cv32f ==> cv32f
|
142 |
+
img_final = pipeline_svg(image, size_value=size_value, level=1, threshold=threshold, kernel_type=kernel_type,
|
143 |
+
dilate_lines_value=dilate_lines_value)
|
144 |
+
|
145 |
+
col2.image(img_final, caption="Image corrigée", use_column_width='auto')
|
146 |
+
|
147 |
+
# Check for grayscale
|
148 |
+
tolerance = 10
|
149 |
+
ratio_of_gray_pixels = int(np.sum((tolerance < image) * (image < 255 - tolerance)) / np.size(image) * 100)
|
150 |
+
if ratio_of_gray_pixels > 1:
|
151 |
+
col3.warning(f":warning: Le nombre de pixels gris est élevé: {ratio_of_gray_pixels} % > 1%")
|
152 |
+
|
153 |
+
# Check reconstruction fidelity
|
154 |
+
distance = np.mean((np.array(image) - img_final) ** 2)
|
155 |
+
if distance > 10:
|
156 |
+
col3.warning(
|
157 |
+
f":warning: Le logo est peut-être trop dégradé (MSE={distance:.2f} > 10).\nVérifier visuellement.")
|
158 |
+
|
159 |
+
dim = (width, height)
|
160 |
+
# resize image
|
161 |
+
resized_img_final = cv2.resize(img_final, dim, interpolation=cv2.INTER_AREA)
|
162 |
+
resized_image_input = cv2.resize(image_input, dim, interpolation=cv2.INTER_AREA)
|
163 |
+
|
164 |
+
buf = BytesIO()
|
165 |
+
# img_stacked = np.hstack((resized_image_input, resized_img_final))
|
166 |
+
img_final = Image.fromarray(resized_image_input).convert("L")
|
167 |
+
img_final.save(buf, format="PNG")
|
168 |
+
byte_im = buf.getvalue()
|
169 |
+
|
170 |
+
btn = col3.download_button(
|
171 |
+
label=":inbox_tray: Télécharger l'image",
|
172 |
+
data=byte_im,
|
173 |
+
file_name=f"corrected_{uploaded_file.name}",
|
174 |
+
mime="image/png"
|
175 |
+
)
|
Dockerfile
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
libcairo2 \
|
10 |
+
libcairo2-dev \
|
11 |
+
&& rm -rf /var/lib/apt/lists/*
|
12 |
+
|
13 |
+
WORKDIR /moulinette
|
14 |
+
COPY . .
|
15 |
+
RUN echo $(ls -1 .. )
|
16 |
+
|
17 |
+
RUN pip3 install -r requirements.txt
|
18 |
+
|
19 |
+
ENTRYPOINT ["streamlit", "run", "Corriger.py", "--server.port=7860"]
|
README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
---
|
2 |
-
title: Moulinette
|
3 |
-
emoji:
|
4 |
colorFrom: gray
|
5 |
colorTo: blue
|
6 |
sdk: streamlit
|
7 |
sdk_version: 1.19.0
|
8 |
-
app_file:
|
9 |
pinned: false
|
10 |
---
|
11 |
|
|
|
1 |
---
|
2 |
+
title: Moulinette
|
3 |
+
emoji: ⚙️
|
4 |
colorFrom: gray
|
5 |
colorTo: blue
|
6 |
sdk: streamlit
|
7 |
sdk_version: 1.19.0
|
8 |
+
app_file: Corriger.py
|
9 |
pinned: false
|
10 |
---
|
11 |
|
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 (3.92 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
|
packages.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
libcairo2
|
2 |
+
libcairo2-dev
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
9 |
+
streamlit-authenticator==0.2.1
|
seguinmoreau.png
ADDED
users.yaml
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
credentials:
|
2 |
+
usernames:
|
3 |
+
hdeferrieres:
|
4 |
+
email: [email protected]
|
5 |
+
name: Hugues de Ferrieres
|
6 |
+
password: $2b$12$mm/Y4S.x.50vtHVDTqEgSO51ne4JpQEVM20Ao8uJXFffje/4xgkr. # To be replaced with hashed password
|
7 |
+
pdeferieres:
|
8 |
+
email: [email protected]
|
9 |
+
name: Paul de Ferrieres
|
10 |
+
password: $2b$12$mm/Y4S.x.50vtHVDTqEgSO51ne4JpQEVM20Ao8uJXFffje/4xgkr. # To be replaced with hashed password
|
11 |
+
seguinmoreau:
|
12 |
+
email: [email protected]
|
13 |
+
name: Seguin Moreau
|
14 |
+
password: $2b$12$mm/Y4S.x.50vtHVDTqEgSO51ne4JpQEVM20Ao8uJXFffje/4xgkr.
|
15 |
+
cookie:
|
16 |
+
expiry_days: 30
|
17 |
+
key: random_signature_key # Must be string
|
18 |
+
name: random_cookie_name
|
19 |
+
preauthorized:
|
20 |
+
emails:
|
21 |