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__":
|