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