Spaces:
Sleeping
Sleeping
add success ribons + difficulty (patch size)
Browse files
app.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
from interactive_pipe import interactive_pipeline, interactive, Image
|
|
|
2 |
import numpy as np
|
3 |
|
4 |
# Helper functions
|
@@ -21,19 +22,50 @@ def get_crop(img, pos_x, pos_y, crop_size=0.1):
|
|
21 |
# -----------------
|
22 |
|
23 |
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
np.random.seed(seed)
|
27 |
pos_x, pos_y = np.random.uniform(0.2, 0.8, 2)
|
28 |
context["puzzle_pos"] = (pos_x, pos_y)
|
29 |
context["puzzle_flip_mirror"] = np.random.choice([True, False], 2)
|
|
|
30 |
|
31 |
|
32 |
-
def create_puzzle(
|
|
|
|
|
|
|
|
|
|
|
33 |
out = img.copy()
|
34 |
x_gt, y_gt = context["puzzle_pos"]
|
35 |
flip_gt, mirror_gt = context["puzzle_flip_mirror"]
|
36 |
-
cs_x, cs_y = get_crop(
|
|
|
37 |
crop = img[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1], ...]
|
38 |
out[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1]] = intensity*crop
|
39 |
crop = flip_image(crop, flip=flip_gt, mirror=mirror_gt)
|
@@ -50,6 +82,7 @@ def flip_mirror_piece(
|
|
50 |
mirror: bool = False,
|
51 |
context: dict = {}
|
52 |
) -> np.ndarray:
|
|
|
53 |
context["user_flip_mirror"] = (flip, mirror)
|
54 |
return flip_image(piece.copy(), flip=flip, mirror=mirror)
|
55 |
|
@@ -65,9 +98,11 @@ def place_puzzle(
|
|
65 |
pos_y: float = 0.5,
|
66 |
context: dict = {}
|
67 |
) -> np.ndarray:
|
|
|
68 |
out = puzzle.copy()
|
69 |
context["user_pos"] = (pos_x, pos_y)
|
70 |
-
cp_x, cp_y = get_crop(
|
|
|
71 |
out[cp_y[0]:cp_y[1], cp_x[0]:cp_x[1]] = piece
|
72 |
return out
|
73 |
|
@@ -79,7 +114,8 @@ TOLERANCE_LEVELS = list(TOLERANCES.keys())
|
|
79 |
@interactive(
|
80 |
tolerance=(TOLERANCE_LEVELS[0], TOLERANCE_LEVELS, "Tolerance")
|
81 |
)
|
82 |
-
def check_puzzle(tolerance: str = "low", context: dict = {}):
|
|
|
83 |
x_gt, y_gt = context["puzzle_pos"]
|
84 |
flip_gt, mirror_gt = context["puzzle_flip_mirror"]
|
85 |
x, y = context["user_pos"]
|
@@ -91,26 +127,30 @@ def check_puzzle(tolerance: str = "low", context: dict = {}):
|
|
91 |
context["success"] = success
|
92 |
|
93 |
|
94 |
-
def
|
|
|
|
|
|
|
|
|
|
|
95 |
success = context["success"]
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
else:
|
100 |
-
return flat_array*np.array([1., 0., 0.])[None, None, :]
|
101 |
|
102 |
# pipeline definition
|
103 |
# -------------------
|
104 |
|
105 |
|
106 |
def captcha_pipe(inp):
|
|
|
107 |
generate_random_puzzle()
|
108 |
puzzle, puzzle_piece = create_puzzle(inp)
|
109 |
puzzle_piece = flip_mirror_piece(puzzle_piece)
|
110 |
puzzle = place_puzzle(puzzle, puzzle_piece)
|
111 |
check_puzzle()
|
112 |
-
|
113 |
-
return
|
114 |
|
115 |
|
116 |
if __name__ == "__main__":
|
|
|
1 |
from interactive_pipe import interactive_pipeline, interactive, Image
|
2 |
+
from typing import Tuple
|
3 |
import numpy as np
|
4 |
|
5 |
# Helper functions
|
|
|
22 |
# -----------------
|
23 |
|
24 |
|
25 |
+
def generate_feedback_ribbon() -> Tuple[np.ndarray, np.ndarray]:
|
26 |
+
"""Generate green and red ribbons for feedback"""
|
27 |
+
flat_array = np.ones((800, 12, 3))
|
28 |
+
colors = [[0., 1., 0.], [1., 0., 0.]]
|
29 |
+
ribbons = [flat_array*np.array(col)[None, None, :] for col in colors]
|
30 |
+
return ribbons[0], ribbons[1]
|
31 |
+
|
32 |
+
|
33 |
+
DIFFICULY = {"easy": 0.18, "medium": 0.1, "hard": 0.05}
|
34 |
+
DIFFICULY_LEVELS = list(DIFFICULY.keys())
|
35 |
+
|
36 |
+
|
37 |
+
@interactive(
|
38 |
+
seed=(45, [0, 100], "Puzzle seed"),
|
39 |
+
difficulty=(DIFFICULY_LEVELS[0], DIFFICULY_LEVELS, "Difficulty")
|
40 |
+
)
|
41 |
+
def generate_random_puzzle(
|
42 |
+
seed: int = 45,
|
43 |
+
difficulty: str = DIFFICULY_LEVELS[0],
|
44 |
+
context: dict = {}
|
45 |
+
):
|
46 |
+
"""Generate random puzzle configuration and store in context.
|
47 |
+
|
48 |
+
Configuration = 2D position and flip/mirror.
|
49 |
+
Freeze seed for reproducibility.
|
50 |
+
"""
|
51 |
np.random.seed(seed)
|
52 |
pos_x, pos_y = np.random.uniform(0.2, 0.8, 2)
|
53 |
context["puzzle_pos"] = (pos_x, pos_y)
|
54 |
context["puzzle_flip_mirror"] = np.random.choice([True, False], 2)
|
55 |
+
context["puzzle_piece_size"] = DIFFICULY.get(difficulty, 0.18)
|
56 |
|
57 |
|
58 |
+
def create_puzzle(
|
59 |
+
img: np.ndarray,
|
60 |
+
intensity: float = 0.4,
|
61 |
+
context: dict = {}
|
62 |
+
) -> Tuple[np.ndarray, np.ndarray]:
|
63 |
+
"""Extract puzzle piece from image. Make a dark hole where the """
|
64 |
out = img.copy()
|
65 |
x_gt, y_gt = context["puzzle_pos"]
|
66 |
flip_gt, mirror_gt = context["puzzle_flip_mirror"]
|
67 |
+
cs_x, cs_y = get_crop(
|
68 |
+
img, x_gt, y_gt, crop_size=context["puzzle_piece_size"])
|
69 |
crop = img[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1], ...]
|
70 |
out[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1]] = intensity*crop
|
71 |
crop = flip_image(crop, flip=flip_gt, mirror=mirror_gt)
|
|
|
82 |
mirror: bool = False,
|
83 |
context: dict = {}
|
84 |
) -> np.ndarray:
|
85 |
+
"""Flip and/or mirror the puzzle piece."""
|
86 |
context["user_flip_mirror"] = (flip, mirror)
|
87 |
return flip_image(piece.copy(), flip=flip, mirror=mirror)
|
88 |
|
|
|
98 |
pos_y: float = 0.5,
|
99 |
context: dict = {}
|
100 |
) -> np.ndarray:
|
101 |
+
"""Place the puzzle piece at the user-defined position."""
|
102 |
out = puzzle.copy()
|
103 |
context["user_pos"] = (pos_x, pos_y)
|
104 |
+
cp_x, cp_y = get_crop(
|
105 |
+
img, pos_x, pos_y, crop_size=context["puzzle_piece_size"])
|
106 |
out[cp_y[0]:cp_y[1], cp_x[0]:cp_x[1]] = piece
|
107 |
return out
|
108 |
|
|
|
114 |
@interactive(
|
115 |
tolerance=(TOLERANCE_LEVELS[0], TOLERANCE_LEVELS, "Tolerance")
|
116 |
)
|
117 |
+
def check_puzzle(tolerance: str = "low", context: dict = {}) -> None:
|
118 |
+
"""Check if the user placed the puzzle piece correctly."""
|
119 |
x_gt, y_gt = context["puzzle_pos"]
|
120 |
flip_gt, mirror_gt = context["puzzle_flip_mirror"]
|
121 |
x, y = context["user_pos"]
|
|
|
127 |
context["success"] = success
|
128 |
|
129 |
|
130 |
+
def display_feedback(
|
131 |
+
puzzle: np.ndarray,
|
132 |
+
ok_ribbon: np.ndarray,
|
133 |
+
nok_ribbon: np.ndarray,
|
134 |
+
context: dict = {}
|
135 |
+
) -> np.ndarray:
|
136 |
success = context["success"]
|
137 |
+
ribbon = ok_ribbon if success else nok_ribbon
|
138 |
+
out = np.hstack([puzzle, ribbon[:puzzle.shape[0], ...]])
|
139 |
+
return out
|
|
|
|
|
140 |
|
141 |
# pipeline definition
|
142 |
# -------------------
|
143 |
|
144 |
|
145 |
def captcha_pipe(inp):
|
146 |
+
ok_ribbon, nok_ribbon = generate_feedback_ribbon()
|
147 |
generate_random_puzzle()
|
148 |
puzzle, puzzle_piece = create_puzzle(inp)
|
149 |
puzzle_piece = flip_mirror_piece(puzzle_piece)
|
150 |
puzzle = place_puzzle(puzzle, puzzle_piece)
|
151 |
check_puzzle()
|
152 |
+
puzzle = display_feedback(puzzle, ok_ribbon, nok_ribbon)
|
153 |
+
return puzzle
|
154 |
|
155 |
|
156 |
if __name__ == "__main__":
|