vkit.engine.seal_impression.text_line_slot_filler
1# Copyright 2022 vkit-x Administrator. All Rights Reserved. 2# 3# This project (vkit-x/vkit) is dual-licensed under commercial and SSPL licenses. 4# 5# The commercial license gives you the full rights to create and distribute software 6# on your own terms without any SSPL license obligations. For more information, 7# please see the "LICENSE_COMMERCIAL.txt" file. 8# 9# This project is also available under Server Side Public License (SSPL). 10# The SSPL licensing is ideal for use cases such as open source projects with 11# SSPL distribution, student/academic purposes, hobby projects, internal research 12# projects without external distribution, or other projects where all SSPL 13# obligations can be met. For more information, please see the "LICENSE_SSPL.txt" file. 14from typing import Sequence, Optional 15import logging 16 17import numpy as np 18 19from vkit.element import Point, Box, ScoreMap 20from vkit.engine.font import TextLine 21from vkit.mechanism.distortion import rotate 22from .type import SealImpression 23 24logger = logging.getLogger(__name__) 25 26 27def fill_text_line_to_seal_impression( 28 seal_impression: SealImpression, 29 text_line_slot_indices: Sequence[int], 30 text_lines: Sequence[TextLine], 31 internal_text_line: Optional[TextLine], 32): 33 score_map = ScoreMap.from_shape(seal_impression.shape) 34 35 assert len(text_line_slot_indices) == len(text_lines) 36 37 for text_line_slot_idx, text_line in zip(text_line_slot_indices, text_lines): 38 if text_line_slot_idx >= len(seal_impression.text_line_slots): 39 logger.error('something wrong.') 40 break 41 42 assert text_line.is_hori 43 assert not text_line.shifted 44 45 ref_char_height = 0 46 ref_char_width = 0 47 for char_glyph in text_line.char_glyphs: 48 if char_glyph.ref_char_height > ref_char_height: 49 ref_char_height = char_glyph.ref_char_height 50 ref_char_width = char_glyph.ref_char_width 51 assert ref_char_height > 0 and ref_char_width > 0 52 53 text_line_slot = seal_impression.text_line_slots[text_line_slot_idx] 54 char_aspect_ratio = text_line_slot.char_aspect_ratio 55 text_line_aspect_ratio = ref_char_width / ref_char_height 56 resized_width_factor = char_aspect_ratio / text_line_aspect_ratio 57 58 for char_slot_idx, (char_box, char_glyph) \ 59 in enumerate(zip(text_line.char_boxes, text_line.char_glyphs)): 60 # Get char slot to be filled. 61 if char_slot_idx >= len(text_line_slot.char_slots): 62 logger.warning('something wrong.') 63 break 64 char_slot = text_line_slot.char_slots[char_slot_idx] 65 66 # Convert char glyph to score map. 67 box = char_box.box 68 resized_width = max(1, round(resized_width_factor * box.width)) 69 char_glyph_shape = (box.height, resized_width) 70 char_score_map = ScoreMap.from_shape((text_line.box.height, resized_width)) 71 72 if char_glyph.score_map: 73 char_glyph_score_map = char_glyph.score_map 74 if char_glyph_score_map.shape != char_glyph_shape: 75 char_glyph_score_map = char_glyph_score_map.to_resized_score_map( 76 resized_height=char_glyph_shape[0], 77 resized_width=char_glyph_shape[1], 78 cv_resize_interpolation=text_line.cv_resize_interpolation, 79 ) 80 with char_score_map.writable_context: 81 char_score_map.mat[box.up:box.down + 1] = char_glyph_score_map.mat 82 83 else: 84 # LCD, fallback to mask. 85 char_glyph_mask = char_glyph.get_glyph_mask( 86 box=box, 87 cv_resize_interpolation=text_line.cv_resize_interpolation, 88 ) 89 if char_glyph_mask.shape != char_glyph_shape: 90 char_glyph_mask = char_glyph_mask.to_resized_mask( 91 resized_height=char_glyph_shape[0], 92 resized_width=char_glyph_shape[1], 93 cv_resize_interpolation=text_line.cv_resize_interpolation, 94 ) 95 with char_score_map.writable_context: 96 char_score_map.mat[box.up:box.down + 1] = char_glyph_mask.mat.astype(np.float32) 97 98 point_up = Point.create(y=0, x=char_score_map.width / 2) 99 100 # Rotate. 101 rotated_result = rotate.distort( 102 # Horizontal text line has angle 270. 103 {'angle': char_slot.angle - 270}, 104 score_map=char_score_map, 105 point=point_up, 106 ) 107 rotated_char_score_map = rotated_result.score_map 108 assert rotated_char_score_map 109 rotated_point_up = rotated_result.point 110 assert rotated_point_up 111 112 # Shift bounding box based on point_up. 113 dst_up = char_slot.point_up.y - rotated_point_up.y 114 dst_down = char_slot.point_up.y + ( 115 rotated_char_score_map.height - 1 - rotated_point_up.y 116 ) 117 dst_left = char_slot.point_up.x - rotated_point_up.x 118 dst_right = char_slot.point_up.x + ( 119 rotated_char_score_map.width - 1 - rotated_point_up.x 120 ) 121 122 # Corner case: out-of-bound. 123 src_up = 0 124 if dst_up < 0: 125 src_up = abs(dst_up) 126 dst_up = 0 127 128 src_down = rotated_char_score_map.height - 1 129 if dst_down >= score_map.height: 130 src_down -= dst_down + 1 - score_map.height 131 dst_down = score_map.height - 1 132 133 assert src_up <= src_down and dst_up <= dst_down 134 assert src_down - src_up == dst_down - dst_up 135 136 src_left = 0 137 if dst_left < 0: 138 src_left = abs(dst_left) 139 dst_left = 0 140 141 src_right = rotated_char_score_map.width - 1 142 if dst_right >= score_map.width: 143 src_right -= dst_right + 1 - score_map.width 144 dst_right = score_map.width - 1 145 146 assert src_left <= src_right and dst_left <= dst_right 147 assert src_right - src_left == dst_right - dst_left 148 149 # Fill. 150 src_box = Box(up=src_up, down=src_down, left=src_left, right=src_right) 151 dst_box = Box(up=dst_up, down=dst_down, left=dst_left, right=dst_right) 152 dst_box.fill_score_map( 153 score_map, 154 src_box.extract_score_map(rotated_char_score_map), 155 keep_max_value=True, 156 ) 157 158 if internal_text_line: 159 internal_text_line_box = seal_impression.internal_text_line_box 160 assert internal_text_line_box 161 162 internal_text_line = internal_text_line.to_shifted_text_line( 163 offset_y=internal_text_line_box.up, 164 offset_x=internal_text_line_box.left, 165 ) 166 if internal_text_line.score_map: 167 internal_text_line.box.fill_score_map(score_map, internal_text_line.score_map) 168 else: 169 internal_text_line.box.fill_score_map(score_map, internal_text_line.mask.mat) 170 171 # Adjust alpha. 172 score_map_max = score_map.mat.max() 173 score_map.assign_mat(score_map.mat * seal_impression.alpha / score_map_max) 174 175 return score_map
def
fill_text_line_to_seal_impression( seal_impression: vkit.engine.seal_impression.type.SealImpression, text_line_slot_indices: Sequence[int], text_lines: Sequence[vkit.engine.font.type.TextLine], internal_text_line: Union[vkit.engine.font.type.TextLine, NoneType]):
28def fill_text_line_to_seal_impression( 29 seal_impression: SealImpression, 30 text_line_slot_indices: Sequence[int], 31 text_lines: Sequence[TextLine], 32 internal_text_line: Optional[TextLine], 33): 34 score_map = ScoreMap.from_shape(seal_impression.shape) 35 36 assert len(text_line_slot_indices) == len(text_lines) 37 38 for text_line_slot_idx, text_line in zip(text_line_slot_indices, text_lines): 39 if text_line_slot_idx >= len(seal_impression.text_line_slots): 40 logger.error('something wrong.') 41 break 42 43 assert text_line.is_hori 44 assert not text_line.shifted 45 46 ref_char_height = 0 47 ref_char_width = 0 48 for char_glyph in text_line.char_glyphs: 49 if char_glyph.ref_char_height > ref_char_height: 50 ref_char_height = char_glyph.ref_char_height 51 ref_char_width = char_glyph.ref_char_width 52 assert ref_char_height > 0 and ref_char_width > 0 53 54 text_line_slot = seal_impression.text_line_slots[text_line_slot_idx] 55 char_aspect_ratio = text_line_slot.char_aspect_ratio 56 text_line_aspect_ratio = ref_char_width / ref_char_height 57 resized_width_factor = char_aspect_ratio / text_line_aspect_ratio 58 59 for char_slot_idx, (char_box, char_glyph) \ 60 in enumerate(zip(text_line.char_boxes, text_line.char_glyphs)): 61 # Get char slot to be filled. 62 if char_slot_idx >= len(text_line_slot.char_slots): 63 logger.warning('something wrong.') 64 break 65 char_slot = text_line_slot.char_slots[char_slot_idx] 66 67 # Convert char glyph to score map. 68 box = char_box.box 69 resized_width = max(1, round(resized_width_factor * box.width)) 70 char_glyph_shape = (box.height, resized_width) 71 char_score_map = ScoreMap.from_shape((text_line.box.height, resized_width)) 72 73 if char_glyph.score_map: 74 char_glyph_score_map = char_glyph.score_map 75 if char_glyph_score_map.shape != char_glyph_shape: 76 char_glyph_score_map = char_glyph_score_map.to_resized_score_map( 77 resized_height=char_glyph_shape[0], 78 resized_width=char_glyph_shape[1], 79 cv_resize_interpolation=text_line.cv_resize_interpolation, 80 ) 81 with char_score_map.writable_context: 82 char_score_map.mat[box.up:box.down + 1] = char_glyph_score_map.mat 83 84 else: 85 # LCD, fallback to mask. 86 char_glyph_mask = char_glyph.get_glyph_mask( 87 box=box, 88 cv_resize_interpolation=text_line.cv_resize_interpolation, 89 ) 90 if char_glyph_mask.shape != char_glyph_shape: 91 char_glyph_mask = char_glyph_mask.to_resized_mask( 92 resized_height=char_glyph_shape[0], 93 resized_width=char_glyph_shape[1], 94 cv_resize_interpolation=text_line.cv_resize_interpolation, 95 ) 96 with char_score_map.writable_context: 97 char_score_map.mat[box.up:box.down + 1] = char_glyph_mask.mat.astype(np.float32) 98 99 point_up = Point.create(y=0, x=char_score_map.width / 2) 100 101 # Rotate. 102 rotated_result = rotate.distort( 103 # Horizontal text line has angle 270. 104 {'angle': char_slot.angle - 270}, 105 score_map=char_score_map, 106 point=point_up, 107 ) 108 rotated_char_score_map = rotated_result.score_map 109 assert rotated_char_score_map 110 rotated_point_up = rotated_result.point 111 assert rotated_point_up 112 113 # Shift bounding box based on point_up. 114 dst_up = char_slot.point_up.y - rotated_point_up.y 115 dst_down = char_slot.point_up.y + ( 116 rotated_char_score_map.height - 1 - rotated_point_up.y 117 ) 118 dst_left = char_slot.point_up.x - rotated_point_up.x 119 dst_right = char_slot.point_up.x + ( 120 rotated_char_score_map.width - 1 - rotated_point_up.x 121 ) 122 123 # Corner case: out-of-bound. 124 src_up = 0 125 if dst_up < 0: 126 src_up = abs(dst_up) 127 dst_up = 0 128 129 src_down = rotated_char_score_map.height - 1 130 if dst_down >= score_map.height: 131 src_down -= dst_down + 1 - score_map.height 132 dst_down = score_map.height - 1 133 134 assert src_up <= src_down and dst_up <= dst_down 135 assert src_down - src_up == dst_down - dst_up 136 137 src_left = 0 138 if dst_left < 0: 139 src_left = abs(dst_left) 140 dst_left = 0 141 142 src_right = rotated_char_score_map.width - 1 143 if dst_right >= score_map.width: 144 src_right -= dst_right + 1 - score_map.width 145 dst_right = score_map.width - 1 146 147 assert src_left <= src_right and dst_left <= dst_right 148 assert src_right - src_left == dst_right - dst_left 149 150 # Fill. 151 src_box = Box(up=src_up, down=src_down, left=src_left, right=src_right) 152 dst_box = Box(up=dst_up, down=dst_down, left=dst_left, right=dst_right) 153 dst_box.fill_score_map( 154 score_map, 155 src_box.extract_score_map(rotated_char_score_map), 156 keep_max_value=True, 157 ) 158 159 if internal_text_line: 160 internal_text_line_box = seal_impression.internal_text_line_box 161 assert internal_text_line_box 162 163 internal_text_line = internal_text_line.to_shifted_text_line( 164 offset_y=internal_text_line_box.up, 165 offset_x=internal_text_line_box.left, 166 ) 167 if internal_text_line.score_map: 168 internal_text_line.box.fill_score_map(score_map, internal_text_line.score_map) 169 else: 170 internal_text_line.box.fill_score_map(score_map, internal_text_line.mask.mat) 171 172 # Adjust alpha. 173 score_map_max = score_map.mat.max() 174 score_map.assign_mat(score_map.mat * seal_impression.alpha / score_map_max) 175 176 return score_map