From aaa01857f08d521a329fa542680c6924e4d52d82 Mon Sep 17 00:00:00 2001 From: Liuweiqing <121866954+14790897@users.noreply.github.com> Date: Fri, 1 Sep 2023 21:34:47 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B6=82=E6=94=B9?= =?UTF-8?q?=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handright/_core.py | 38 ++++++++++++++++++++++++++++++++++++-- handright/_template.py | 19 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/handright/_core.py b/handright/_core.py index 0a0cb77..cf70ebe 100644 --- a/handright/_core.py +++ b/handright/_core.py @@ -120,6 +120,24 @@ def _draw_page( if (x > width - right_margin - font_size and text[start] not in end_chars): break + + # 随机选择一个字符进行替换 9.1 + if random.choice([True, False]): + wrong_char_index = random.randint(0, len(text) - 1) + + # 检查字符是否在排除列表中 + while text[wrong_char_index] in end_chars: + wrong_char_index = random.randint(0, len(text) - 1) + wrong_char = text[wrong_char_index] + + origin_x =x + # 绘制替换字 + x = _flow_layout(draw, x, y, wrong_char, tpl, rand) + + # 添加涂改标记(斜线) + _draw_strikethrough(draw, origin_x, y, tpl, rand) + + # 绘制正确的字 if Feature.GRID_LAYOUT in tpl.get_features(): x = _grid_layout(draw, x, y, text[start], tpl, rand) else: @@ -130,6 +148,22 @@ def _draw_page( y += line_spacing return start +def _draw_strikethrough(draw, x, y, tpl, rand): + line_length = tpl.get_font().size + length_sigma = tpl.get_strikethrough_length_sigma() + angle_sigma = tpl.get_strikethrough_angle_sigma() + width_sigma = tpl.get_strikethrough_width_sigma() + + # 添加扰动 + actual_length = line_length + gauss(rand, 0, length_sigma) + initial_angle = 45 # 初始角度设置为45度 + actual_angle = initial_angle + gauss(rand, 0, angle_sigma) + actual_width = 5 + gauss(rand, 0, width_sigma) # 假设基础宽度为1 + + + end_x = x + actual_length * math.cos(math.radians(actual_angle)) + end_y = y + actual_length * math.sin(math.radians(actual_angle)) + draw.line((x, y, end_x, end_y), fill=_WHITE, width=int(actual_width)) def _flow_layout( draw, x, y, char, tpl: Template, rand: random.Random @@ -246,10 +280,10 @@ def _extract_stroke( bitmap, start: Tuple[int, int], strokes, bbox: Tuple[int, int, int, int] ) -> None: """Helper function of _extract_strokes() which uses depth first search to - find the pixels of a glyph.""" + find the pixels of a glyph. 修改了传入的 strokes 参数""" left, upper, right, lower = bbox stack = [start, ] - while stack: + while stack:#白色是1,為true x, y = stack.pop() if y - 1 >= upper and bitmap[x, y - 1] and strokes.add(_xy(x, y - 1)): stack.append((x, y - 1)) diff --git a/handright/_template.py b/handright/_template.py index 7d97697..abe701e 100644 --- a/handright/_template.py +++ b/handright/_template.py @@ -32,6 +32,9 @@ class Template(object): "_perturb_x_sigma", "_perturb_y_sigma", "_perturb_theta_sigma", + "_strikethrough_length_sigma", + "_strikethrough_angle_sigma", + "_strikethrough_width_sigma", "_features", ) @@ -46,6 +49,7 @@ class Template(object): _DEFAULT_END_CHARS = ",。》?;:’”】}、!%),.>?;:]}!%)′″℃℉" _DEFAULT_PERTURB_THETA_SIGMA = 0.07 + _DEFAULT_FEATURES = frozenset() @@ -68,6 +72,9 @@ def __init__( perturb_x_sigma: Optional[float] = None, perturb_y_sigma: Optional[float] = None, perturb_theta_sigma: float = _DEFAULT_PERTURB_THETA_SIGMA, + strikethrough_length_sigma: Optional[float]=2, + strikethrough_angle_sigma: Optional[float]=2, + strikethrough_width_sigma: Optional[float]=2, features: Set = _DEFAULT_FEATURES, ): """Note that, all the Integer parameters are in pixels. @@ -117,6 +124,9 @@ def __init__( self.set_perturb_x_sigma(perturb_x_sigma) self.set_perturb_y_sigma(perturb_y_sigma) self.set_perturb_theta_sigma(perturb_theta_sigma) + self._strikethrough_length_sigma = strikethrough_length_sigma + self._strikethrough_angle_sigma = strikethrough_angle_sigma + self._strikethrough_width_sigma = strikethrough_width_sigma self.set_features(features) def __eq__(self, other) -> bool: @@ -290,6 +300,15 @@ def get_perturb_y_sigma(self) -> float: def get_perturb_theta_sigma(self) -> float: return self._perturb_theta_sigma + + def get_strikethrough_length_sigma(self): + return self._strikethrough_length_sigma + + def get_strikethrough_angle_sigma(self): + return self._strikethrough_angle_sigma + + def get_strikethrough_width_sigma(self): + return self._strikethrough_width_sigma def get_size(self) -> Tuple[int, int]: return self.get_background().size From 4a9ab5c42b20c0d372eec88fb0cbc54ea36b7a85 Mon Sep 17 00:00:00 2001 From: Liuweiqing <121866954+14790897@users.noreply.github.com> Date: Thu, 7 Sep 2023 14:26:53 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B6=82=E6=94=B9?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handright/_core.py | 32 ++++++++++-------- handright/_template.py | 76 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/handright/_core.py b/handright/_core.py index cf70ebe..b672944 100644 --- a/handright/_core.py +++ b/handright/_core.py @@ -109,7 +109,7 @@ def _draw_page( while y <= height - bottom_margin - font_size: x = left_margin while True: - if text[start] == _LF: + if text[start] == _LF: #跳过换行符自己处理 start += 1 if start == len(text): return start @@ -122,18 +122,20 @@ def _draw_page( break # 随机选择一个字符进行替换 9.1 - if random.choice([True, False]): + if rand.random() < tpl.get_strikethrough_probability(): wrong_char_index = random.randint(0, len(text) - 1) - + wrong_end_chars = end_chars + ' ' # 检查字符是否在排除列表中 - while text[wrong_char_index] in end_chars: + while text[wrong_char_index] in wrong_end_chars: wrong_char_index = random.randint(0, len(text) - 1) wrong_char = text[wrong_char_index] origin_x =x - # 绘制替换字 - x = _flow_layout(draw, x, y, wrong_char, tpl, rand) - + # 绘制错误的字(被划掉的字) + if Feature.GRID_LAYOUT in tpl.get_features(): + x = _grid_layout(draw, x, y, wrong_char, tpl, rand) + else: + x = _flow_layout(draw, x, y, wrong_char, tpl, rand) # 添加涂改标记(斜线) _draw_strikethrough(draw, origin_x, y, tpl, rand) @@ -149,21 +151,23 @@ def _draw_page( return start def _draw_strikethrough(draw, x, y, tpl, rand): - line_length = tpl.get_font().size + line_length = tpl.get_font().size * math.sqrt(2) length_sigma = tpl.get_strikethrough_length_sigma() angle_sigma = tpl.get_strikethrough_angle_sigma() width_sigma = tpl.get_strikethrough_width_sigma() + line_width = tpl.get_strikethrough_width() + start_x = x + 1/7*line_length + start_y = y + 1/7*line_length # 添加扰动 actual_length = line_length + gauss(rand, 0, length_sigma) initial_angle = 45 # 初始角度设置为45度 actual_angle = initial_angle + gauss(rand, 0, angle_sigma) - actual_width = 5 + gauss(rand, 0, width_sigma) # 假设基础宽度为1 - + actual_width = line_width + gauss(rand, 0, width_sigma) # 假设基础宽度为5 - end_x = x + actual_length * math.cos(math.radians(actual_angle)) - end_y = y + actual_length * math.sin(math.radians(actual_angle)) - draw.line((x, y, end_x, end_y), fill=_WHITE, width=int(actual_width)) + end_x = start_x + actual_length * math.cos(math.radians(actual_angle))*5/7 + end_y = start_y + actual_length * math.sin(math.radians(actual_angle))*5/7 + draw.line((start_x, start_y, end_x, end_y), fill=_WHITE, width=int(actual_width)) def _flow_layout( draw, x, y, char, tpl: Template, rand: random.Random @@ -186,7 +190,7 @@ def _grid_layout( round(gauss(rand, y, tpl.get_line_spacing_sigma()))) font = _get_font(tpl, rand) _ = _draw_char(draw, char, xy, font) - x += tpl.get_word_spacing() + tpl.get_font().size + x += tpl.get_word_spacing() + tpl.get_font().size#主要的区别在于这里的X它是固定的 return x diff --git a/handright/_template.py b/handright/_template.py index abe701e..201d92b 100644 --- a/handright/_template.py +++ b/handright/_template.py @@ -35,6 +35,8 @@ class Template(object): "_strikethrough_length_sigma", "_strikethrough_angle_sigma", "_strikethrough_width_sigma", + "_strikethrough_probability", + "_strikethrough_width", "_features", ) @@ -50,6 +52,10 @@ class Template(object): _DEFAULT_PERTURB_THETA_SIGMA = 0.07 + _DEFAULT_strikethrough_width_sigma = 2 + _DEFAULT_strikethrough_angle_sigma = 2 + _DEFAULT_strikethrough_probability = 0.03 + _DEFAULT_FEATURES = frozenset() @@ -72,9 +78,11 @@ def __init__( perturb_x_sigma: Optional[float] = None, perturb_y_sigma: Optional[float] = None, perturb_theta_sigma: float = _DEFAULT_PERTURB_THETA_SIGMA, - strikethrough_length_sigma: Optional[float]=2, - strikethrough_angle_sigma: Optional[float]=2, - strikethrough_width_sigma: Optional[float]=2, + strikethrough_length_sigma: Optional[float]=None, + strikethrough_angle_sigma: float=_DEFAULT_strikethrough_angle_sigma, + strikethrough_width_sigma: Optional[float]=None, + strikethrough_probability: float=_DEFAULT_strikethrough_probability, + strikethrough_width: Optional[float]=None, features: Set = _DEFAULT_FEATURES, ): """Note that, all the Integer parameters are in pixels. @@ -124,9 +132,11 @@ def __init__( self.set_perturb_x_sigma(perturb_x_sigma) self.set_perturb_y_sigma(perturb_y_sigma) self.set_perturb_theta_sigma(perturb_theta_sigma) - self._strikethrough_length_sigma = strikethrough_length_sigma - self._strikethrough_angle_sigma = strikethrough_angle_sigma - self._strikethrough_width_sigma = strikethrough_width_sigma + self.set_strikethrough_length_sigma(strikethrough_length_sigma) + self.set_strikethrough_angle_sigma(strikethrough_angle_sigma) + self.set_strikethrough_width_sigma(strikethrough_width_sigma) + self.set_strikethrough_probability(strikethrough_probability) + self.set_strikethrough_width(strikethrough_width) self.set_features(features) def __eq__(self, other) -> bool: @@ -148,7 +158,12 @@ def __eq__(self, other) -> bool: and self._end_chars == other._end_chars and self._perturb_x_sigma == other._perturb_x_sigma and self._perturb_y_sigma == other._perturb_y_sigma - and self._perturb_theta_sigma == other._perturb_theta_sigma) + and self._perturb_theta_sigma == other._perturb_theta_sigma + and self._strikethrough_length_sigma == other._strikethrough_length_sigma + and self._strikethrough_angle_sigma == other._strikethrough_angle_sigma + and self._strikethrough_width_sigma == other._strikethrough_width_sigma + and self._strikethrough_probability == other._strikethrough_probability + and self._strikethrough_width == other._strikethrough_width) def set_background(self, background: PIL.Image.Image) -> None: self._background = background @@ -247,6 +262,40 @@ def set_perturb_theta_sigma( ) -> None: self._perturb_theta_sigma = perturb_theta_sigma + def set_strikethrough_length_sigma( + self, strikethrough_length_sigma: Optional[float] = None + ) -> None: + if strikethrough_length_sigma is None: + self._strikethrough_length_sigma = self._font.size / 32 + else: + self._strikethrough_length_sigma = strikethrough_length_sigma + + def set_strikethrough_width_sigma( + self, strikethrough_width_sigma: Optional[float] = None + ) -> None: + if strikethrough_width_sigma is None: + self._strikethrough_width_sigma = self._font.size / 32 + else: + self._strikethrough_width_sigma = strikethrough_width_sigma + + def set_strikethrough_probability( + self, strikethrough_probability: float = _DEFAULT_strikethrough_probability + ) -> None: + self._strikethrough_probability = strikethrough_probability + + def set_strikethrough_width( + self, strikethrough_width: Optional[float] = None + ) -> None: + if strikethrough_width is None: + self._strikethrough_width = self._font.size / 32 + else: + self._strikethrough_width = strikethrough_width + + def set_strikethrough_angle_sigma( + self, strikethrough_angle_sigma: float = _DEFAULT_strikethrough_angle_sigma + )-> None: + self._strikethrough_angle_sigma = strikethrough_angle_sigma + def get_background(self) -> PIL.Image.Image: return self._background @@ -309,6 +358,12 @@ def get_strikethrough_angle_sigma(self): def get_strikethrough_width_sigma(self): return self._strikethrough_width_sigma + + def get_strikethrough_probability(self): + return self._strikethrough_probability + + def get_strikethrough_width(self): + return self._strikethrough_width def get_size(self) -> Tuple[int, int]: return self.get_background().size @@ -339,7 +394,12 @@ def __repr__(self) -> str: "end_chars={self._end_chars}, " "perturb_x_sigma={self._perturb_x_sigma}, " "perturb_y_sigma={self._perturb_y_sigma}, " - "perturb_theta_sigma={self._perturb_theta_sigma})" + "perturb_theta_sigma={self._perturb_theta_sigma}," + "strikethrough_length_sigma == {self._strikethrough_length_sigma}," + "strikethrough_angle_sigma == {self._strikethrough_angle_sigma}," + "strikethrough_width_sigma == {self._strikethrough_width_sigma}," + "strikethrough_probability == {self._strikethrough_probability}," + "strikethrough_width == {self._strikethrough_width})" ).format(class_name=class_name, self=self) From ffd4bb12820865cc663fdd0401a9405873e782f7 Mon Sep 17 00:00:00 2001 From: Liuweiqing <121866954+14790897@users.noreply.github.com> Date: Thu, 7 Sep 2023 14:35:05 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B6=82=E6=94=B9?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handright/_template.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handright/_template.py b/handright/_template.py index 201d92b..9a30730 100644 --- a/handright/_template.py +++ b/handright/_template.py @@ -54,7 +54,7 @@ class Template(object): _DEFAULT_strikethrough_width_sigma = 2 _DEFAULT_strikethrough_angle_sigma = 2 - _DEFAULT_strikethrough_probability = 0.03 + _DEFAULT_strikethrough_probability = 0 _DEFAULT_FEATURES = frozenset() @@ -79,9 +79,9 @@ def __init__( perturb_y_sigma: Optional[float] = None, perturb_theta_sigma: float = _DEFAULT_PERTURB_THETA_SIGMA, strikethrough_length_sigma: Optional[float]=None, - strikethrough_angle_sigma: float=_DEFAULT_strikethrough_angle_sigma, + strikethrough_angle_sigma: float = _DEFAULT_strikethrough_angle_sigma, strikethrough_width_sigma: Optional[float]=None, - strikethrough_probability: float=_DEFAULT_strikethrough_probability, + strikethrough_probability: float = _DEFAULT_strikethrough_probability, strikethrough_width: Optional[float]=None, features: Set = _DEFAULT_FEATURES, ):