diff --git a/.gitignore b/.gitignore index 6aea1d4..1eef3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ ENV/ # Rope project settings .ropeproject +data/ diff --git a/README.md b/README.md index acb16ad..7e1ae08 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,33 @@ # SynthText +This a modified version of [Ankush's code](https://github.com/ankush-me/SynthText) for generating synthetic text images which support right-to-left languages such as Persian and Arabic. + +As in Persian and Arabic, words are made using connected letters, this code only outputs the word level bounding boxes. + +**A sample output of the code** +![Persian Synthetic Scene-Text Samples](sample_fa.png "Persian Synthetic Samples") + +## Install the necessary libs and create a conda environment +First install the `fribidi` library + +``` +sudo apt install libfribidi-dev +``` + +Then create a conda environment from the `environment.yml` file: + +``` +conda env create -f environment.yml +``` + +Activate the environment with the following command: + +``` +conda activate gentext +``` + +The rest of the README is from the original repository +------------------ + Code for generating synthetic text images as described in ["Synthetic Data for Text Localisation in Natural Images", Ankush Gupta, Andrea Vedaldi, Andrew Zisserman, CVPR 2016](http://www.robots.ox.ac.uk/~vgg/data/scenetext/). diff --git a/colorize3_poisson.py b/colorize3_poisson.py index e4f32af..ee94ef5 100644 --- a/colorize3_poisson.py +++ b/colorize3_poisson.py @@ -17,6 +17,11 @@ def sample_weighted(p_dict): ps = p_dict.keys() return ps[np.random.choice(len(ps),p=p_dict.values())] +def rgb_color_diff_in_gray(col1, col2): + gray1 = col1[0]*0.299 + col1[1]*0.587 + col1[2]*0.114 + gray2 = col2[0]*0.299 + col2[1]*0.587 + col2[2]*0.114 + return abs(gray1 - gray2) + class Layer(object): def __init__(self,alpha,color): @@ -45,6 +50,7 @@ def __init__(self,alpha,color): class FontColor(object): def __init__(self, col_file): + self.gray_diff_threshold = 25 with open(col_file,'rb') as f: #self.colorsRGB = cp.load(f) u = pickle._Unpickler(f) @@ -90,10 +96,25 @@ def sample_from_data(self, bg_mat): col1 = self.sample_normal(data_col[:3],data_col[3:6]) col2 = self.sample_normal(data_col[6:9],data_col[9:12]) + ## fix as follows + true_bg_col = np.mean(np.mean(bg_orig, axis=0), axis=0) if nn < self.ncol: + fg_col = col2 + diff = rgb_color_diff_in_gray(fg_col, true_bg_col) + while diff < self.gray_diff_threshold: + #print 'change color' + fg_col = np.random.choice(256, 3).astype('uint8') + diff = rgb_color_diff_in_gray(fg_col, true_bg_col) + col2 = fg_col return (col2, col1) else: # need to swap to make the second color close to the input backgroun color + fg_col = col1 + diff = rgb_color_diff_in_gray(fg_col, true_bg_col) + while diff < self.gray_diff_threshold: + fg_col = np.random.choice(256, 3).astype('uint8') + diff = rgb_color_diff_in_gray(fg_col, true_bg_col) + col1 = fg_col return (col1, col2) def mean_color(self, arr): diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..0037054 --- /dev/null +++ b/environment.yml @@ -0,0 +1,69 @@ +name: gentext +channels: + - defaults +dependencies: + - _libgcc_mutex=0.1=main + - blas=1.0=mkl + - bzip2=1.0.6=h14c3975_5 + - ca-certificates=2019.5.15=0 + - cairo=1.14.12=h8948797_3 + - certifi=2019.6.16=py36_0 + - ffmpeg=4.0=hcdf2ecd_0 + - fontconfig=2.13.0=h9420a91_0 + - freeglut=3.0.0=hf484d3e_5 + - freetype=2.9.1=h8a8886c_1 + - glib=2.56.2=hd408876_0 + - graphite2=1.3.13=h23475e2_0 + - harfbuzz=1.8.8=hffaf4a1_0 + - hdf5=1.10.2=hba1933b_1 + - icu=58.2=h9c2bf20_1 + - intel-openmp=2019.4=243 + - jasper=2.0.14=h07fcdf6_1 + - jpeg=9b=h024ee3a_2 + - libedit=3.1.20181209=hc058e9b_0 + - libffi=3.2.1=hd88cf55_4 + - libgcc-ng=8.2.0=hdf63c60_1 + - libgfortran-ng=7.3.0=hdf63c60_0 + - libglu=9.0.0=hf484d3e_1 + - libopencv=3.4.2=hb342d67_1 + - libopus=1.3=h7b6447c_0 + - libpng=1.6.37=hbc83047_0 + - libstdcxx-ng=8.2.0=hdf63c60_1 + - libtiff=4.0.10=h2733197_2 + - libuuid=1.0.3=h1bed415_2 + - libvpx=1.7.0=h439df22_0 + - libxcb=1.13=h1bed415_1 + - libxml2=2.9.9=hea5a465_1 + - mkl=2019.4=243 + - mkl_fft=1.0.12=py36ha843d7b_0 + - mkl_random=1.0.2=py36hd81dba3_0 + - ncurses=6.1=he6710b0_1 + - opencv=3.4.2=py36h6fd60c2_1 + - openssl=1.0.2s=h7b6447c_0 + - pcre=8.43=he6710b0_0 + - pip=19.0.3=py36_0 + - pixman=0.38.0=h7b6447c_0 + - py-opencv=3.4.2=py36hb342d67_1 + - python=3.6.5=hc3d631a_2 + - readline=7.0=h7b6447c_5 + - setuptools=40.8.0=py36_0 + - sqlite=3.26.0=h7b6447c_0 + - tk=8.6.8=hbc83047_0 + - wheel=0.33.1=py36_0 + - xz=5.2.4=h14c3975_4 + - zlib=1.2.11=h7b6447c_3 + - zstd=1.3.7=h0b5b093_0 + - pip: + - cycler==0.10.0 + - h5py==2.7.1 + - matplotlib==2.1.1 + - numpy==1.16.4 + - pillow==5.0.0 + - pygame==1.9.3 + - pyparsing==2.2.0 + - python-dateutil==2.6.1 + - pytz==2017.3 + - scapy==2.4.2 + - scipy==1.0.0 + - wget==3.2 + diff --git a/fribidi.py b/fribidi.py new file mode 100644 index 0000000..0ef5b39 --- /dev/null +++ b/fribidi.py @@ -0,0 +1,1000 @@ +""" +an implementation of Unicode Bidirectional Algorithm, using GNU FriBidi + +This is a python wrap of GNU FriBidi C library. +http://fribidi.org/ + +GNU FriBidi is an implementation of Unicode Bidirectional Algorithm (bidi). +http://unicode.org/reports/tr9/ + + +This file is originally written by behnam at https://github.com/behnam/python-fribidi. +I just slightly modified it to support python3 + +""" + + +import ctypes + + +# Load FriBidi + +_libfribidi = ctypes.CDLL("libfribidi.so") + +try: + _libfribidi.fribidi_shape() +except AttributeError: + libfribidi_version = '0.10' + libfribidi_version_major = 0 + libfribidi_version_minor = 10 +else: + libfribidi_version = '0.19' + libfribidi_version_major = 0 + libfribidi_version_minor = 19 + + +# Versions + +VERSION = '0.10' +"Version of the python wrapper." + + +# Memory allocation functions + +def _malloc_int_array(n): + + """ + Return a pointer to allocated C int array of length `n'. + """ + + t = ctypes.c_int * n + return t() + + +def _malloc_int8_array(n): + + """ + Return a pointer to allocated C int array of length `n'. + """ + + t = ctypes.c_int8 * n + return t() + + +def _malloc_int32_array(n): + + """ + Return a pointer to allocated C int32 array of length `n'. + """ + + t = ctypes.c_uint32 * n + return t() + + +def _malloc_int32_array_from_list(a, n=None): + + """ + Return a pointer to allocated C int32 array of length `n', initialized with `a' + + If `n' is not set, the length of `a' will be considered. + """ + + if n is None: + n = len(a) + + # Memory allocations + + m = _malloc_int32_array(n) + + for i in range(n): + m[i] = a[i] + + return m + + +def _malloc_char_array(n): + + """ + Return a pointer to allocated UTF8 (C char) array of length `n'. + """ + + t = ctypes.c_char * n + return t() + + +def _malloc_char_array_from_string(s): + + """ + Return a pointer to allocated UTF8 (C char) array, initialized with `s'. + """ + + return ctypes.c_char_p(s) + + +# Unicode type convertors + +def _pyunicode_to_utf32_p(a_pyunicode): + """Return UTF32 (C int32) array from Py_Unicode. + """ + + a_len = len(a_pyunicode) + + utf8_pystr = a_pyunicode.encode('UTF-8') + utf8_len = len(utf8_pystr) + utf8_p = _malloc_char_array_from_string(utf8_pystr) + + utf32_p = _malloc_int32_array(a_len+1) + _libfribidi.fribidi_utf8_to_unicode(utf8_p, utf8_len, utf32_p) + + return utf32_p + + +def _utf32_p_to_pyunicode(a_utf32_p): + """Return Py_Unicode from UTF32 (C int32) array. + """ + + utf32_len = ctypes.sizeof(a_utf32_p) // ctypes.sizeof(ctypes.c_uint32) + + utf8_len = 6*utf32_len+1 + utf8_p = _malloc_char_array(utf8_len) + + _libfribidi.fribidi_unicode_to_utf8(a_utf32_p, utf32_len, utf8_p) + + return utf8_p.value.decode('UTF-8') + + +# ######################################################################## +# FriBidi API, Bidi, Types (fribidi-bidi-types.h) + +# Character and Paragraph Masks and Types + +class Mask: + """ + TODO. + """ + + # Mask values + + RTL = 0x00000001 # Is right to left + ARABIC = 0x00000002 # Is arabic + + # Each character can be only one of the three following: + STRONG = 0x00000010 # Is strong + WEAK = 0x00000020 # Is weak + NEUTRAL = 0x00000040 # Is neutral + SENTINEL = 0x00000080 # Is sentinel + # Sentinels are not valid chars, just identify the start/end of strings. + + # Each charcter can be only one of the five following: + LETTER = 0x00000100 # Is letter: L, R, AL + NUMBER = 0x00000200 # Is number: EN, AN + NUMSEPTER = 0x00000400 # Is number separator or terminator: ES, ET, CS + SPACE = 0x00000800 # Is space: BN, BS, SS, WS + EXPLICIT = 0x00001000 # Is expilict mark: LRE, RLE, LRO, RLO, PDF + + # Can be set only if Mask.SPACE is also set. + SEPARATOR = 0x00002000 # Is test separator: BS, SS + + OVERRIDE = 0x00004000 # Is explicit override: LRO, RLO + + # The following must be to make types pairwise different, some of them can + # be removed but are here because of efficiency (make queries faster). + ES = 0x00010000 + ET = 0x00020000 + CS = 0x00040000 + + NSM = 0x00080000 + BN = 0x00100000 + + BS = 0x00200000 + SS = 0x00400000 + WS = 0x00800000 + + # We reserve a single bit for user's private use: we will never use it. + PRIVATE = 0x01000000 + + +class _Type: + """ + TODO. + """ + + # Strong types + + LTR = Mask.STRONG + Mask.LETTER # Left-To-Right letter + RTL = Mask.STRONG + Mask.LETTER + Mask.RTL # Right-To-Left letter + AL = Mask.STRONG + Mask.LETTER + Mask.RTL + Mask.ARABIC # Arabic Letter + LRE = Mask.STRONG + Mask.EXPLICIT # Left-to-Right Embedding + RLE = Mask.STRONG + Mask.EXPLICIT + Mask.RTL # Right-to-Left Embedding + LRO = Mask.STRONG + Mask.EXPLICIT + Mask.OVERRIDE # Left-to-Right Override + RLO = Mask.STRONG + Mask.EXPLICIT + Mask.RTL + Mask.OVERRIDE # Right-to-Left Override + + # Weak types + + PDF = Mask.WEAK + Mask.EXPLICIT # Pop Directional Override + EN = Mask.WEAK + Mask.NUMBER # European Numeral + AN = Mask.WEAK + Mask.NUMBER + Mask.ARABIC # Arabic Numeral + ES = Mask.WEAK + Mask.NUMSEPTER + Mask.ES # European number Separator + ET = Mask.WEAK + Mask.NUMSEPTER + Mask.ET # European number Terminator + CS = Mask.WEAK + Mask.NUMSEPTER + Mask.CS # Common Separator + NSM = Mask.WEAK + Mask.NSM # Non Spacing Mark + BN = Mask.WEAK + Mask.SPACE + Mask.BN # Boundary Neutral + + # Neutral types + + BS = Mask.NEUTRAL + Mask.SPACE + Mask.SEPARATOR + Mask.BS # Block Separator + SS = Mask.NEUTRAL + Mask.SPACE + Mask.SEPARATOR + Mask.SS # Segment Separator + WS = Mask.NEUTRAL + Mask.SPACE + Mask.WS # WhiteSpace + ON = Mask.NEUTRAL # Other Neutral + + # Paragraph-only types + + WLTR = Mask.WEAK # Weak Left-To-Right + WRTL = Mask.WEAK | Mask.RTL # Weak Right-To-Left + + SENTINEL = Mask.SENTINEL # start or end of text (run list) SENTINEL + # Only used internally + + PRIVATE = Mask.PRIVATE # Private types for applications + # More private types can be obtained by summing up from this one + + +class CharType: + """ + Class of character (direction) types. + + Strong types: + + LTR Left-To-Right letter + RTL Right-To-Left letter + AL Arabic Letter + LRE Left-to-Right Embedding + RLE Right-to-Left Embedding + LRO Left-to-Right Override + RLO Right-to-Left Override + + Weak types: + + PDF Pop Directional Override + EN European Numeral + AN Arabic Numeral + ES European number Separator + ET European number Terminator + CS Common Separator + NSM Non Spacing Mark + BN Boundary Neutral + + Neutral types: + + BS Block Separator + SS Segment Separator + WS WhiteSpace + ON Other Neutral + + """ + + LTR = _Type.LTR + RTL = _Type.RTL + AL = _Type.AL + EN = _Type.EN + AN = _Type.AN + ES = _Type.ES + ET = _Type.ET + CS = _Type.CS + NSM = _Type.NSM + BN = _Type.BN + BS = _Type.BS + SS = _Type.SS + WS = _Type.WS + ON = _Type.ON + LRE = _Type.LRE + RLE = _Type.RLE + LRO = _Type.LRO + RLO = _Type.RLO + PDF = _Type.PDF + + +class ParType: + + """ + Class of paragraph (direction) types: + + LTR Left-to-Right paragraph + RTL Right-to-Left paragraph + ON (Other) Neutral paragraph + WLTR Weak Left-to-Right paragraph + WRTL Weak Right-to-Left paragraph + + """ + + LTR = _Type.LTR + RTL = _Type.RTL + ON = _Type.ON + WLTR = _Type.WLTR + WRTL = _Type.WRTL + + +def level_is_rtl(lev): + + """ + Return True if `lev' is a Right-to-Left level, False otherwise. + + """ + + return lev & 1 + + +def level_to_dir(lev): + + """ + Return the bidi type corresponding to the direction of the level number. + + Return ParType.LTR for evens, and ParType.RTL for odds. + + """ + + return ParType.RTL if level_is_rtl(lev) else ParType.LTR + + +def dir_is_rtl(dir): + + """ + Return True if `dir' is a Right-to-Left, False otherwise. + + """ + + return dir & Mask.RTL + + +def dir_to_level(dir): + + """ + Return the minimum level of the direction. + + Return 0 for LTR and 1 for RTL. + + """ + + return 1 if dir_is_rtl(dir) else 0 + +# TODO: More dir functions (probabely should put into Type class) +# fribidi-bidi-types.h: 283-334 + + +# Functions + +def get_bidi_types(unicode_text, text_length=None): + + """ + Return characters bidi types. + + This function returns the bidi type of a character as defined in Table 3.7 + Bidirectional Character Types of the Unicode Bidirectional Algorithm + available at + http://www.unicode.org/reports/tr9/#Bidirectional_Character_Types, using + data provided in file UnicodeData.txt of the Unicode Character Database + available at http://www.unicode.org/Public/UNIDATA/UnicodeData.txt . + + """ + # TOD: There are a few macros defined in fribidi-bidi-types.h for querying a bidi type. + + + if not isinstance(unicode_text, str): + unicode_text = unicode(unicode_text) + + if not text_length: + text_length = len(unicode_text) + + # Memory allocations + + input_utf32_p = _pyunicode_to_utf32_p(unicode_text) + + output_chartype_p = _malloc_int32_array(text_length) + + # Calling the API + + """ + FRIBIDI_ENTRY void fribidi_get_bidi_types ( + const FriBidiChar *str, /* input string */ + const FriBidiStrIndex len, /* input string length */ + FriBidiCharType *btypes /* output bidi types */ + ); + """ + + if libfribidi_version_minor <= 10: + _libfribidi.fribidi_get_types( + input_utf32_p, # input string + text_length, # input string length + output_chartype_p # output bidi types + ) + + else: + _libfribidi.fribidi_get_bidi_types( + input_utf32_p, # input string + text_length, # input string length + output_chartype_p # output bidi types + ) + + # Pythonizing the output + + return [i for i in output_chartype_p] + + +# TODO: fribidi_get_bidi_type_name + + +# ######################################################################## +# FriBidi API, Bidi (fribidi-bidi.h) + + +def get_par_direction(bidi_types_list, text_length=None): + + """ + Return base paragraph direction + + No weak paragraph direction is returned, only LTR, RTL, or ON. + + Input: List of bidi types as returned by get_bidi_types() + + This function finds the base direction of a single paragraph, + as defined by rule P2 of the Unicode Bidirectional Algorithm available at + http://www.unicode.org/reports/tr9/#P2. + + You typically do not need this function as get_par_embedding_levels() knows + how to compute base direction itself, but you may need this to implement a + more sophisticated paragraph direction handling. + + Note that you can pass more than a paragraph to this function and the + direction of the first non-neutral paragraph is returned, which is a very + good heuristic to set direction of the neutral paragraphs at the beginning + of text. For other neutral paragraphs, you better use the direction of the + previous paragraph. + + """ + + if not text_length: + text_length = len(bidi_types_list) + + # Memory allocations + + input_bidi_types_p = _malloc_int32_array_from_list(bidi_types_list, text_length) + + # Calling the API + + ''' + FRIBIDI_ENTRY FriBidiParType fribidi_get_par_direction ( + const FriBidiCharType *bidi_types, /* input list of bidi types as returned by fribidi_get_bidi_types() */ + const FriBidiStrIndex len /* input string length */ + ); + ''' + + par_type = _libfribidi.fribidi_get_par_direction( + input_bidi_types_p, # input bidi types + text_length # input string length + ) + + # Pythonizing the output + + return int(par_type) + + +def get_par_embedding_levels(bidi_types_list, text_length=None, + base_direction=None): + + """ + Return the list of embedding levels of characters in the paragraph. + + A tuple of list of embedding levels, resolved paragraph direction, and + maximum embedding level will be returned. + + Input: List of bidi types as returned by get_bidi_types() + + This function finds the bidi embedding levels of a single paragraph, + as defined by the Unicode Bidirectional Algorithm available at + http://www.unicode.org/reports/tr9/. This function implements rules P2 to + I1 inclusive, and parts 1 to 3 of L1, except for rule X9 which is + implemented in remove_bidi_marks(). Part 4 of L1 is implemented in + reorder_line(). + + """ + + # TODO: There are a few macros defined in fribidi-bidi-types.h to work with this embedding levels. + + if not text_length: + text_length = len(bidi_types_list) + + if base_direction is None: + base_direction=ParType.LTR + + # Memory allocations + + input_bidi_types_p = _malloc_int32_array_from_list(bidi_types_list, text_length) + pbase_dir_p = ctypes.pointer(ctypes.c_int32(base_direction)) + + emb_p = _malloc_int8_array(text_length) + + # Calling the API + + """ + FRIBIDI_ENTRY FriBidiLevel fribidi_get_par_embedding_levels ( + const FriBidiCharType *bidi_types, /* input list of bidi types as returned by fribidi_get_bidi_types() */ + const FriBidiStrIndex len, /* input string length of the paragraph */ + FriBidiParType *pbase_dir, /* requested and resolved paragraph base direction */ + FriBidiLevel *embedding_levels /* output list of embedding levels */ + ) FRIBIDI_GNUC_WARN_UNUSED; + """ + + success = _libfribidi.fribidi_get_par_embedding_levels( + input_bidi_types_p, # input list of bidi types as returned by get_bidi_types() + text_length, # input string length of the paragraph + pbase_dir_p, # requested and resolved paragraph base direction + emb_p # output list of embedding levels + ) + + if not success: + raise Exception('fribidi_get_par_embedding_levels failed') + + # Pythonizing the output + + max_levels = success - 1 + + output_levels_list = [i for i in emb_p] + + return [output_levels_list, pbase_dir_p[0], max_levels] + + +def reorder_line(bidi_types_list, text_length=None, line_offset=0, + base_direction=None, with_max_levels=False): + + """ + Return visual reordered of a line of logical string + """ + + # TODO + + """ + This function reorders the characters in a line of text from logical to + final visual order. This function implements part 4 of rule L1, and rules + L2 and L3 of the Unicode Bidirectional Algorithm available at + http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels. + + As a side effect it also sets position maps if not NULL. + + You should provide the resolved paragraph direction and embedding levels as + set by fribidi_get_par_embedding_levels(). Also note that the embedding + levels may change a bit. To be exact, the embedding level of any sequence + of white space at the end of line is reset to the paragraph embedding level + (That is part 4 of rule L1). + + Note that the bidi types and embedding levels are not reordered. You can + reorder these (or any other) arrays using the map later. The user is + responsible to initialize map to something sensible, like an identity + mapping, or pass NULL if no map is needed. + + There is an optional part to this function, which is whether non-spacing + marks for right-to-left parts of the text should be reordered to come after + their base characters in the visual string or not. Most rendering engines + expect this behavior, but console-based systems for example do not like it. + This is controlled by the FRIBIDI_FLAG_REORDER_NSM flag. The flag is on + in FRIBIDI_FLAGS_DEFAULT. + + Returns: Maximum level found in this line plus one, or zero if any error + occured (memory allocation failure most probably). + + """ + + if not text_length: + text_length = len(bidi_types_list) + + if base_direction is None: + base_direction=ParType.LTR + + # Memory allocations + + input_bidi_types_p = _malloc_int32_array_from_list(bidi_types_list, text_length) + pbase_dir_p = ctypes.pointer(ctypes.c_int32(base_direction)) + + emb_p = _malloc_int8_array(text_length) + + # Calling the API + + """ + FRIBIDI_ENTRY FriBidiLevel fribidi_reorder_line ( + FriBidiFlags flags, /* reorder flags */ + const FriBidiCharType *bidi_types, /* input list of bidi types as returned by fribidi_get_bidi_types() */ + const FriBidiStrIndex len, /* input length of the line */ + const FriBidiStrIndex off, /* input offset of the beginning of the line in the paragraph */ + const FriBidiParType base_dir, /* resolved paragraph base direction */ + FriBidiLevel *embedding_levels, /* input list of embedding levels, as returned by fribidi_get_par_embedding_levels */ + FriBidiChar *visual_str, /* visual string to reorder */ + FriBidiStrIndex *map /* a map of string indices which is reordered to reflect where each glyph ends up. */ + ) FRIBIDI_GNUC_WARN_UNUSED; + """ + + success = _libfribidi.fribidi_get_par_embedding_levels( + input_flags, # reorder flags + input_bidi_types_p, # input list of bidi types as returned by get_bidi_types() + text_length, # input string length of the paragraph + line_offset, # input offset of the beginning of the line in the paragraph + pbase_dir_p, # requested and resolved paragraph base direction + emb_p, # output list of embedding levels + embedding_levels_p # input list of embedding levels, as returned by get_par_embedding_levels() + ) + + if not success: + raise Exception('fribidi_get_par_embedding_levels failed') + + # Pythonizing the output + + max_levels = success - 1 + + output_levels_list = [i for i in emb_p] + + if with_max_levels: + return [output_levels_list, max_levels] + else: + return output_levels_list + + + +# ######################################################################## +# FriBidi API, Misc + +def log2vis(unicode_text, text_length=None, base_direction=None, + with_l2v_position=False, with_v2l_position=False, + with_embedding_level=False): + """ + Return a unicode text contaning the visual order of characters in the text. + + If paragraph direction is not set (`base_direction'), it will be assumed to + to be letf-to-right (LTR). + + If any of with_l2v_position, with_v2l_position, and with_embedding_level + are True, the return value will be a tuple including logical-to-visual + position, visual-to-logical positions, or embedding-level lists + respectively. + + """ + + if not isinstance(unicode_text, str): + unicode_text = unicode(unicode_text) + + if not text_length: + text_length = len(unicode_text) + + if base_direction is None: + base_direction=ParType.LTR + + + # Memory allocations + + input_utf32_p = _pyunicode_to_utf32_p(unicode_text) + pbase_dir_p = ctypes.pointer(ctypes.c_int32(base_direction)) + + output_utf32_p = _malloc_int32_array(text_length+1) + + l2v_p = _malloc_int_array(text_length) if with_l2v_position else None + v2l_p = _malloc_int_array(text_length) if with_v2l_position else None + emb_p = _malloc_int8_array(text_length) if with_embedding_level else None + + # Calling the API + + """ + FRIBIDI_API fribidi_boolean fribidi_log2vis ( + + /* input */ + FriBidiChar *str, + FriBidiStrIndex len, + FriBidiCharType *pbase_dirs, + + /* output */ + FriBidiChar *visual_str, + FriBidiStrIndex *position_L_to_V_list, + FriBidiStrIndex *position_V_to_L_list, + FriBidiLevel *embedding_level_list + ); + """ + + successed = _libfribidi.fribidi_log2vis( + # input + input_utf32_p, + text_length, + pbase_dir_p, + + # output + output_utf32_p, + l2v_p, + v2l_p, + emb_p + ) + + if not successed: + raise Exception('fribidi_log2vis failed') + + # Pythonizing the output + + output_u = _utf32_p_to_pyunicode(output_utf32_p) + + if with_l2v_position or with_v2l_position or with_embedding_level: + res = (output_u, ) + if with_l2v_position: res += ([i for i in l2v_p], ) + if with_v2l_position: res += ([i for i in v2l_p], ) + if with_embedding_level: res += ([i for i in emb_p], ) + + else: + res = output_u + + return res + + +def log2vis_get_embedding_levels(unicode_text, base_direction=None): + + """ + Return an array containing the embedding-level of characters in the text. + + """ + + if not isinstance(unicode_text, str): + unicode_text = unicode(unicode_text) + + if base_direction is None: + base_direction=ParType.LTR + + text_len = len(unicode_text) + + # Memory allocations + + input_utf32_p = _pyunicode_to_utf32_p(unicode_text) + pbase_dir_p = ctypes.pointer(ctypes.c_int32(base_direction)) + + emb_p = _malloc_int8_array(text_len) + + # Calling the API + + """ + FRIBIDI_API fribidi_boolean fribidi_log2vis_get_embedding_levels ( + + /* input */ + FriBidiChar *str, + FriBidiStrIndex len, + FriBidiCharType *pbase_dir, + + /* output */ + FriBidiLevel *embedding_level_list + ); + """ + + successed = _libfribidi.fribidi_log2vis_get_embedding_levels( + # input + input_utf32_p, + text_len, + pbase_dir_p, + + # output + emb_p + ) + + if not successed: + raise Exception('fribidi_log2vis_get_embedding_levels failed') + + # Pythonizing the output + + res = [i for i in emb_p] + + return res + + +def remove_bidi_marks(unicode_text, text_length=None, + with_position_to=False, with_position_from=False, + with_embedding_level=False): + + """ + Return the text with all Bidirectional Marks removed. + + If with_position_to, with_position_from, or with_embedding_level are True, + the return value will be a tuple including positions from input text to + output text, positions from output text to input text, or embedding-level + lists respectively. + + Note: Seems the optional parameters of fribidi_remove_bidi_marks() doesn't + work or crash. Use them at your own risk. + + """ + + if not isinstance(unicode_text, str): + unicode_text = unicode(unicode_text) + + if not text_length: + text_length = len(unicode_text) + + # Memory allocations + + input_utf32_p = _pyunicode_to_utf32_p(unicode_text) + + pto_p = _malloc_int_array(text_length) if with_position_to else None + pfr_p = _malloc_int_array(text_length) if with_position_from else None + emb_p = _malloc_int8_array(text_length) if with_embedding_level else None + + # Calling the API + + """ + FRIBIDI_API FriBidiStrIndex fribidi_remove_bidi_marks ( + + /* input & output */ + FriBidiChar *str, + + /* input */ + FriBidiStrIndex length, + + /* output */ + FriBidiStrIndex *position_to_this_list, + FriBidiStrIndex *position_from_this_list, + FriBidiLevel *embedding_level_list + ); + """ + + new_length = _libfribidi.fribidi_remove_bidi_marks( + # input & output + input_utf32_p, + + # input + text_length, + + # output + pto_p, + pfr_p, + emb_p + ) + + + # Pythonizing the output + + output_u = _utf32_p_to_pyunicode(input_utf32_p) + + if with_position_to or with_position_from or with_embedding_level: + res = (output_u, ) + if with_position_to: res += ([i for i in pto_p], ) + if with_position_from: res += ([i for i in pfr_p], ) + if with_embedding_level: res += ([i for i in emb_p], ) + + else: + res = output_u + + return res + + +def get_mirror_chars(unicode_text): + + """ + Return TODO + + TODO. + + * fribidi_get_mirror_char() returns the mirrored character, if input + * character has a mirror, or the input itself. + * if mirrored_ch is NULL, just returns if character has a mirror or not. + + """ + + if not isinstance(unicode_text, str): + unicode_text = unicode(unicode_text) + + res = u'' + + for unicode_char in unicode_text: + text_length = len(unicode_text) + + # Memory allocations + + input_utf32_p = _pyunicode_to_utf32_p(unicode_char) + + output_utf32_p = _malloc_int32_array(text_length+1) + + # Calling the API + + """ + FRIBIDI_API fribidi_boolean fribidi_get_mirror_char ( + + /* Input */ + FriBidiChar ch, + + /* Output */ + FriBidiChar *mirrored_ch + ); + """ + + _libfribidi.fribidi_get_mirror_char( + # input + input_utf32_p[0], + # output + output_utf32_p + ) + + # Pythonizing the output + + res += _utf32_p_to_pyunicode(output_utf32_p) + + return res + + +def get_mirror_prop(unicode_text): + + """ + Return TODO + + TODO. + + * fribidi_get_mirror_char() returns the mirrored character, if input + * character has a mirror, or the input itself. + * if mirrored_ch is NULL, just returns if character has a mirror or not. + + """ + + if not isinstance(unicode_text, str): + unicode_text = unicode(unicode_text) + + res = [] + + for unicode_char in unicode_text: + + # Memory allocations + + input_utf32_p = _pyunicode_to_utf32_p(unicode_char) + + # Calling the API + + """ + FRIBIDI_API fribidi_boolean fribidi_get_mirror_char ( + + /* Input */ + FriBidiChar ch, + + /* Output */ + FriBidiChar *mirrored_ch + ); + """ + + is_mirror = _libfribidi.fribidi_get_mirror_char( + # input + input_utf32_p[0], + # output + None + ) + + # Pythonizing the output + + res.append(is_mirror) + + return res + + +def get_version_info(): + + """ + Return TODO + + TODO. + + """ + + # TODO + + return str(_libfribidi.fribidi_version_info) + + +# Main + +def _main(): + + """ + Print visual representation of command-line parameters (as a whole). + + """ + + import sys + text = ' '.join(sys.argv[1:]) + print(log2vis(text)) + + +if __name__=='__main__': + _main() diff --git a/gen.py b/gen.py index ffaf995..264b39c 100644 --- a/gen.py +++ b/gen.py @@ -30,7 +30,7 @@ DATA_PATH = 'data' DB_FNAME = osp.join(DATA_PATH,'dset.h5') # url of the data (google-drive public file): -DATA_URL = 'http://www.robots.ox.ac.uk/~ankush/data.tar.gz' +DATA_URL = 'https://www.dropbox.com/s/nagp2q3twqtyi02/data.zip?dl=1' OUT_FILE = 'results/SynthText.h5' def get_data(): @@ -40,7 +40,7 @@ def get_data(): """ if not osp.exists(DB_FNAME): try: - colorprint(Color.BLUE,'\tdownloading data (56 M) from: '+DATA_URL,bold=True) + colorprint(Color.BLUE,'\tdownloading data (24 M) from: '+DATA_URL,bold=True) print() sys.stdout.flush() out_fname = 'data.tar.gz' @@ -72,7 +72,7 @@ def add_res_to_db(imgname,res,db): db['data'][dname].attrs['wordBB'] = res[i]['wordBB'] #db['data'][dname].attrs['txt'] = res[i]['txt'] L = res[i]['txt'] - L = [n.encode("ascii", "ignore") for n in L] + L = [n.encode("utf-8", "ignore") for n in L] db['data'][dname].attrs['txt'] = L diff --git a/poisson_reconstruct.py b/poisson_reconstruct.py index 7f90899..aeb2282 100644 --- a/poisson_reconstruct.py +++ b/poisson_reconstruct.py @@ -87,7 +87,7 @@ def poisson_solve(gx,gy,bnd): return img -def blit_images(im_top,im_back,scale_grad=1.0,mode='max'): +def blit_images(im_top,im_back,scale_grad=1.0,mode='src'): """ combine images using poission editing. IM_TOP and IM_BACK should be of the same size. @@ -212,7 +212,7 @@ def contiguous_regions(mask): with sns.axes_style("darkgrid"): plt.subplot(2,1,2) plt.plot(l_alpha,label='alpha') - plt.hold(True) + #plt.hold(True) plt.plot(l_poisson,label='poisson') plt.plot(l_actual,label='actual') plt.legend() @@ -227,7 +227,7 @@ def contiguous_regions(mask): with sns.axes_style("white"): plt.subplot(2,1,1) plt.imshow(im_alpha[:,:,::-1].astype('uint8')) - plt.hold(True) + #plt.hold(True) plt.plot([0,im_alpha_L.shape[0]-1],[i,i],'r') plt.axis('image') plt.show() diff --git a/prep_scripts/floodFill.py b/prep_scripts/floodFill.py index 1f61ef1..8440d6a 100644 --- a/prep_scripts/floodFill.py +++ b/prep_scripts/floodFill.py @@ -17,7 +17,7 @@ def get_seed(sx,sy,ucm): n = sx.size - for i in xrange(n): + for i in range(n): if ucm[sx[i]+1,sy[i]+1] == 0: return (sy[i],sx[i]) @@ -41,7 +41,7 @@ def get_mask(ucm,viz=False): sx,sy = np.where(mask==0) seed = get_seed(sx,sy,ucm) i += 1 - print " > terminated in %d steps"%i + print(" > terminated in %d steps"%i) if viz: plt.imshow(mask) @@ -81,7 +81,7 @@ def get_imname(self,i): return "".join(map(chr, self.ucm_h5[self.ucm_h5['names'][0,self.i]][:])) def __stop__(self): - print "DONE" + print("DONE") self.ucm_h5.close() raise StopIteration @@ -101,14 +101,14 @@ def get_valid_name(self): def next(self): imname = self.get_valid_name() - print "%d of %d"%(self.i+1,self.N) + print ("%d of %d"%(self.i+1,self.N)) ucm = self.ucm_h5[self.ucm_h5['ucms'][0,self.i]][:] ucm = ucm.copy() self.i += 1 return ((ucm>self.th).astype('uint8'),imname) ucm_iter = ucm_iterable(db_path,th) - print "cpu count: ", mp.cpu_count() + print ("cpu count: ", mp.cpu_count()) parpool = mp.Pool(4) ucm_result = parpool.imap_unordered(get_mask_parallel, ucm_iter, chunksize=1) @@ -116,16 +116,16 @@ def next(self): if res is None: continue ((mask,area,label),imname) = res - print "got back : ", imname + print ("got back : ", imname) mask = mask.astype('uint16') mask_dset = dbo_mask.create_dataset(imname, data=mask) mask_dset.attrs['area'] = area mask_dset.attrs['label'] = label # close the h5 files: - print "closing DB" + print ("closing DB") dbo.close() - print ">>>> DONE" + print (">>>> DONE") base_dir = '/home/' # directory containing the ucm.mat, i.e., output of run_ucm.m diff --git a/invert_font_size.py b/prep_scripts/invert_font_size.py similarity index 55% rename from invert_font_size.py rename to prep_scripts/invert_font_size.py index 5697467..6407185 100644 --- a/invert_font_size.py +++ b/prep_scripts/invert_font_size.py @@ -4,10 +4,10 @@ import pygame from pygame import freetype -from text_utils import FontState +import os import numpy as np import matplotlib.pyplot as plt -import cPickle as cp +import pickle as cp pygame.init() @@ -19,21 +19,21 @@ xs = [] models = {} #linear model -FS = FontState() -#plt.figure() -#plt.hold(True) -for i in xrange(len(FS.fonts)): - print i - font = freetype.Font(FS.fonts[i], size=12) +FONT_LIST = './data/fonts/fontlist.txt' +fonts = [os.path.join('./data/fonts',f.strip()) for f in open(FONT_LIST)] +##plt.hold(True) +for i in range(len(fonts)): + print(fonts[i]) + font = freetype.Font(fonts[i], size=12) h = [] for y in ys: - h.append(font.get_sized_glyph_height(y)) + h.append(font.get_sized_glyph_height(int(y))) h = np.array(h) m,_,_,_ = np.linalg.lstsq(A,h) models[font.name] = m xs.append(h) -with open('font_px2pt.cp','w') as f: +with open('./data/models/font_px2pt.cp','wb') as f: cp.dump(models,f) #plt.plot(xs,ys[i]) #plt.show() diff --git a/prep_scripts/update_freq.py b/prep_scripts/update_freq.py new file mode 100644 index 0000000..c1453e9 --- /dev/null +++ b/prep_scripts/update_freq.py @@ -0,0 +1,21 @@ +from collections import Counter +import pickle + +cnt = 0 +filename = './data/newsgroup/newsgroup.txt' +with open(filename, 'r') as f: + c = Counter() + for line in f.readlines(): + c += Counter(line.strip()) + cnt += len(line.strip()) + # print c +print(cnt) + +for key in c: + c[key] = float(c[key]) / cnt + print(key, c[key]) + +d = dict(c) +# print d +with open("./data/models/char_freq.cp", 'wb') as f: + pickle.dump(d, f) \ No newline at end of file diff --git a/sample_fa.png b/sample_fa.png new file mode 100644 index 0000000..e17469c Binary files /dev/null and b/sample_fa.png differ diff --git a/synthgen.py b/synthgen.py index 2fda5e9..bf2477c 100644 --- a/synthgen.py +++ b/synthgen.py @@ -271,16 +271,16 @@ def get_text_placement_mask(xyz,mask,plane,pad=2,viz=False): H,_ = cv2.findHomography(pts[0].astype('float32').copy(), pts_fp_i32[0].astype('float32').copy(), method=0) - Hinv,_ = cv2.findHomography(pts_fp_i32[0].astype('float32').copy(), pts[0].astype('float32').copy(), method=0) + if viz: plt.subplot(1,2,1) plt.imshow(mask) plt.subplot(1,2,2) plt.imshow(~place_mask) - plt.hold(True) + #plt.hold(True) for i in range(len(pts_fp_i32)): plt.scatter(pts_fp_i32[i][:,0],pts_fp_i32[i][:,1], edgecolors='none',facecolor='g',alpha=0.5) @@ -350,7 +350,7 @@ def viz_textbb(fignum,text_im, bb_list,alpha=1.0): plt.close(fignum) plt.figure(fignum) plt.imshow(text_im) - plt.hold(True) + #plt.hold(True) H,W = text_im.shape[:2] for i in range(len(bb_list)): bbs = bb_list[i] @@ -443,6 +443,7 @@ def bb_filter(self,bb0,bb,text): bb : 2x4xn matrix of BB after perspective text: string of text -- for excluding symbols/punctuations. """ + # return True h0 = np.linalg.norm(bb0[:,3,:] - bb0[:,0,:], axis=0) w0 = np.linalg.norm(bb0[:,1,:] - bb0[:,0,:], axis=0) hw0 = np.c_[h0,w0] @@ -450,14 +451,16 @@ def bb_filter(self,bb0,bb,text): h = np.linalg.norm(bb[:,3,:] - bb[:,0,:], axis=0) w = np.linalg.norm(bb[:,1,:] - bb[:,0,:], axis=0) hw = np.c_[h,w] - + + if np.sum(hw0[:,1] == 0) > 0 or np.sum(hw[:,1] == 0) > 0: + return False # remove newlines and spaces: - text = ''.join(text.split()) - assert len(text)==bb.shape[-1] + # text = ''.join(text.split()) + # assert len(text)==bb.shape[-1] - alnum = np.array([ch.isalnum() for ch in text]) - hw0 = hw0[alnum,:] - hw = hw[alnum,:] + # alnum = np.array([ch.isalnum() for ch in text]) + # hw0 = hw0[alnum,:] + # hw = hw[alnum,:] min_h0, min_h = np.min(hw0[:,0]), np.min(hw[:,0]) asp0, asp = hw0[:,0]/hw0[:,1], hw[:,0]/hw[:,1] @@ -470,29 +473,32 @@ def bb_filter(self,bb0,bb,text): return is_good - def get_min_h(selg, bb, text): + def get_min_h(self, bb, text): + # return True # find min-height: h = np.linalg.norm(bb[:,3,:] - bb[:,0,:], axis=0) # remove newlines and spaces: text = ''.join(text.split()) - assert len(text)==bb.shape[-1] + # assert len(text)==bb.shape[-1] - alnum = np.array([ch.isalnum() for ch in text]) - h = h[alnum] + # alnum = np.array([ch.isalnum() for ch in text]) + # h = h[alnum] return np.min(h) def feather(self, text_mask, min_h): # determine the gaussian-blur std: - if min_h <= 15 : - bsz = 0.25 - ksz=1 - elif 15 < min_h < 30: - bsz = max(0.30, 0.5 + 0.1*np.random.randn()) - ksz = 3 - else: - bsz = max(0.5, 1.5 + 0.5*np.random.randn()) - ksz = 5 + bsz = 0.5 * np.random.rand() + ksz = np.random.choice([1,3,5]) + # if min_h <= 15 : + # bsz = 0.25 + # ksz=1 + # elif 15 < min_h < 30: + # bsz = max(0.30, 0.5 + 0.1*np.random.randn()) + # ksz = 3 + # else: + # bsz = max(0.5, 1.5 + 0.5*np.random.randn()) + # ksz = 5 return cv2.GaussianBlur(text_mask,(ksz,ksz),bsz) def place_text(self,rgb,collision_mask,H,Hinv): @@ -549,34 +555,34 @@ def char2wordBB(self, charBB, text): output : 2x4xm matrix of BB coordinates, where, m == number of words. """ - wrds = text.split() - bb_idx = np.r_[0, np.cumsum([len(w) for w in wrds])] - wordBB = np.zeros((2,4,len(wrds)), 'float32') + # wrds = text.split() + # bb_idx = np.r_[0, np.cumsum([len(w) for w in wrds])] + # wordBB = np.zeros((2,4,len(wrds)), 'float32') - for i in range(len(wrds)): - cc = charBB[:,:,bb_idx[i]:bb_idx[i+1]] - - # fit a rotated-rectangle: - # change shape from 2x4xn_i -> (4*n_i)x2 - cc = np.squeeze(np.concatenate(np.dsplit(cc,cc.shape[-1]),axis=1)).T.astype('float32') - rect = cv2.minAreaRect(cc.copy()) - box = np.array(cv2.boxPoints(rect)) - - # find the permutation of box-coordinates which - # are "aligned" appropriately with the character-bb. - # (exhaustive search over all possible assignments): - cc_tblr = np.c_[cc[0,:], - cc[-3,:], - cc[-2,:], - cc[3,:]].T - perm4 = np.array(list(itertools.permutations(np.arange(4)))) - dists = [] - for pidx in range(perm4.shape[0]): - d = np.sum(np.linalg.norm(box[perm4[pidx],:]-cc_tblr,axis=1)) - dists.append(d) - wordBB[:,:,i] = box[perm4[np.argmin(dists)],:].T - - return wordBB + # for i in range(len(wrds)): + # cc = charBB[:,:,bb_idx[i]:bb_idx[i+1]] + + # # fit a rotated-rectangle: + # # change shape from 2x4xn_i -> (4*n_i)x2 + # cc = np.squeeze(np.concatenate(np.dsplit(cc,cc.shape[-1]),axis=1)).T.astype('float32') + # rect = cv2.minAreaRect(cc.copy()) + # box = np.array(cv2.boxPoints(rect)) + + # # find the permutation of box-coordinates which + # # are "aligned" appropriately with the character-bb. + # # (exhaustive search over all possible assignments): + # cc_tblr = np.c_[cc[0,:], + # cc[-3,:], + # cc[-2,:], + # cc[3,:]].T + # perm4 = np.array(list(itertools.permutations(np.arange(4)))) + # dists = [] + # for pidx in range(perm4.shape[0]): + # d = np.sum(np.linalg.norm(box[perm4[pidx],:]-cc_tblr,axis=1)) + # dists.append(d) + # wordBB[:,:,i] = box[perm4[np.argmin(dists)],:].T + + return charBB def render_text(self,rgb,depth,seg,area,label,ninstance=1,viz=False): @@ -689,5 +695,5 @@ def render_text(self,rgb,depth,seg,area,label,ninstance=1,viz=False): viz_masks(2,img,seg,depth,regions['label']) # viz_regions(rgb.copy(),xyz,seg,regions['coeff'],regions['label']) if i < ninstance-1: - raw_input(colorize(Color.BLUE,'continue?',True)) + input(colorize(Color.BLUE,'continue?',True)) return res diff --git a/text_utils.py b/text_utils.py index 1814eda..0c0ccfc 100644 --- a/text_utils.py +++ b/text_utils.py @@ -16,6 +16,8 @@ import math from common import * import pickle +import codecs +import fribidi def sample_weighted(p_dict): ps = list(p_dict.keys()) @@ -137,17 +139,14 @@ def render_multiline(self,font,text): for l in lines: x = 0 # carriage-return y += line_spacing # line-feed - - for ch in l: # render each character - if ch.isspace(): # just shift - x += space.width - else: - # render the character - ch_bounds = font.render_to(surf, (x,y), ch) - ch_bounds.x = x + ch_bounds.x - ch_bounds.y = y - ch_bounds.y - x += ch_bounds.width - bbs.append(np.array(ch_bounds)) + words = l.split(' ') + for word in words: + word = fribidi.log2vis(word, None, fribidi.ParType.RTL) + ch_bounds = font.render_to(surf, (x,y), word) + ch_bounds.x = x + ch_bounds.x + ch_bounds.y = y - ch_bounds.y + x += ch_bounds.width + space.width + bbs.append(np.array(ch_bounds)) # get the union of characters for cropping: r0 = pygame.Rect(bbs[0]) @@ -167,6 +166,8 @@ def render_curved(self, font, word_text): """ use curved baseline for rendering word """ + # word_text = word_text[::-1] + word_text = word_text.replace('\u200c', ' ') wl = len(word_text) isword = len(word_text.split())==1 @@ -174,6 +175,7 @@ def render_curved(self, font, word_text): if not isword or wl > 10 or np.random.rand() > self.p_curved: return self.render_multiline(font, word_text) + word_text = fribidi.log2vis(word_text, None, fribidi.ParType.RTL) # create the surface: lspace = font.get_sized_height() + 1 lbound = font.get_rect(word_text) @@ -186,52 +188,23 @@ def render_curved(self, font, word_text): curve = [BS['curve'](i-mid_idx) for i in range(wl)] curve[mid_idx] = -np.sum(curve) / (wl-1) rots = [-int(math.degrees(math.atan(BS['diff'](i-mid_idx)/(font.size/2)))) for i in range(wl)] - + bbs = [] # place middle char - rect = font.get_rect(word_text[mid_idx]) + rect = font.get_rect(word_text) rect.centerx = surf.get_rect().centerx rect.centery = surf.get_rect().centery + rect.height rect.centery += curve[mid_idx] - ch_bounds = font.render_to(surf, rect, word_text[mid_idx], rotation=rots[mid_idx]) + ch_bounds = font.render_to(surf, rect, word_text, rotation=rots[mid_idx]) ch_bounds.x = rect.x + ch_bounds.x ch_bounds.y = rect.y - ch_bounds.y mid_ch_bb = np.array(ch_bounds) # render chars to the left and right: - last_rect = rect ch_idx = [] - for i in range(wl): - #skip the middle character - if i==mid_idx: - bbs.append(mid_ch_bb) - ch_idx.append(i) - continue + bbs.append(mid_ch_bb) + ch_idx.append(0) - if i < mid_idx: #left-chars - i = mid_idx-1-i - elif i==mid_idx+1: #right-chars begin - last_rect = rect - - ch_idx.append(i) - ch = word_text[i] - - newrect = font.get_rect(ch) - newrect.y = last_rect.y - if i > mid_idx: - newrect.topleft = (last_rect.topright[0]+2, newrect.topleft[1]) - else: - newrect.topright = (last_rect.topleft[0]-2, newrect.topleft[1]) - newrect.centery = max(newrect.height, min(fsize[1] - newrect.height, newrect.centery + curve[i])) - try: - bbrect = font.render_to(surf, newrect, ch, rotation=rots[i]) - except ValueError: - bbrect = font.render_to(surf, newrect, ch) - bbrect.x = newrect.x + bbrect.x - bbrect.y = newrect.y - bbrect.y - bbs.append(np.array(bbrect)) - last_rect = newrect - # correct the bounding-box order: bbs_sequence_order = [None for i in ch_idx] for idx,i in enumerate(ch_idx): @@ -522,9 +495,13 @@ def __init__(self, min_nchar, fn): 'LINE':self.sample_line, 'PARA':self.sample_para} - with open(fn,'r') as f: - self.txt = [l.strip() for l in f.readlines()] - + # with open(fn,'r') as f: + # self.txt = [l.strip() for l in f.readlines()] + with codecs.open(fn, 'r', "utf-8") as f: + self.txt = [] + for line in f: + self.txt.append(line.strip()) + # distribution over line/words for LINE/PARA: self.p_line_nline = np.array([0.85, 0.10, 0.05]) self.p_line_nword = [4,3,12] # normal: (mu, std) diff --git a/visualize_results.py b/visualize_results.py index 7b578c6..9bb547a 100644 --- a/visualize_results.py +++ b/visualize_results.py @@ -24,7 +24,7 @@ def viz_textbb(text_im, charBB_list, wordBB, alpha=1.0): plt.close(1) plt.figure(1) plt.imshow(text_im) - plt.hold(True) + #plt.hold(True) H,W = text_im.shape[:2] # plot the character-BB: @@ -58,7 +58,7 @@ def main(db_fname): rgb = db['data'][k][...] charBB = db['data'][k].attrs['charBB'] wordBB = db['data'][k].attrs['wordBB'] - txt = db['data'][k].attrs['txt'] + txt = ", ".join(a.decode('utf-8') for a in db['data'][k].attrs['txt']) viz_textbb(rgb, [charBB], wordBB) print ("image name : ", colorize(Color.RED, k, bold=True))