1

I'm trying to convert a list of numbers that I believe represent bytes that together constitute a bitmap image, into said image file (saved to disk) and/or simply converted into a form usable by tesseract. I'd prefer to be able to visualize the images, though, to make sure the conversion actually worked properly. I don't know the shape of the image, but I think it might be 4 wide by 8 tall.

I have this json file of a character mapping for a font (an image-based font, used by a Japanese dictionary) where each character is represented as a bitmap image, e.g. one character is:

                       { "bitmap": [0,0,26,0,17,252,17,36,89,100,81,84,81,132,209,252,144,0,19,254,42,84,46,84,38,84,66,86,79,255,0,0], "code": 46370 }

I'm trying to get at the actual characters these represent. The way I'm trying to go about this is by converting these lists of ints into bytes (or arrays of bytes that they represent), then converting them into bitmap image files (and possible saving them to disk - that's the step I'm stuck at), and then I'll OCR those images (either in python with tesseract or maybe with Adobe's OCR if I can put them in a pdf) to determine their UTF-8 or Shift-JIS equivalents. If I'm overcomplicating this then I'd also appreciate some more direction!

I've referred to the following stackoverflow posts (and a few others) to try and convert the list of ints into an actual image file: How do I convert byte array to bitmap image in Python? Converting int to bytes in Python 3 Image from bytes (python) PIL: Convert Bytearray to Image Convert Numpy array of ASCII codes to string

I've also tried this library and I think I successfully converted the list into a string representing bits, and into this library's version of a bitmap, but I can't figure out how to save the resulting object. Looking at the source, this particular library's bitmap class seems kind of useless for what I want to do.

The numbers above supposedly correspond to this picture: enter image description here (which is not greyscale).

I've written something that converts a list of ints into either bytes or a "bytearray" (I've just tried a lot of different things, and I'm not sure which format I actually need), but then I get stuck when I try to save those bytes as a bmp file. Depending on what I try, I get errors like the following:

OSError: cannot identify image file 'out.bmp'
OSError: cannot identify image file <_io.BytesIO object at 0x000001F037F7C5C8>
AttributeError: 'BitMap' object has no attribute 'save'

or, I just save a file that can't be opened because it's an unsupported file format (e.g. if I just open a file and write to it).

I'm guessing part of the problem is that I'm not saving data that uses bitmap headers. But also, saving some bytes as an image seems a lot more complicated than I thought, so frankly, I don't even know where to begin.

I'm also not sure if the byte array I'm making is an array of the individual bytes or some representation of the whole list...

Can someone help me save this list of numbers as an image? I don't know if I actually need to save it as a bitmap.

This is (one version of) my program:

import io
from PIL import Image

test_image = "out.bmp"
test_bytes = [0,0,26,0,17,252,17,36,89,100,81,84,81,132,209,252,144,0,19,254,42,84,46,84,38,84,66,86,79,255,0,0]
actual_bytes = bytes(test_bytes)

def generate_output_image(input_image):
    image = Image.open(io.BytesIO(input_image))
    image.save(test_image)

generate_output_image(actual_bytes)
weirdalsuperfan
  • 163
  • 1
  • 2
  • 8
  • I completely fail to understand what you are trying to do! What are the numbers in your list? What will be the output? Are you trying to make a picture? Or a font? Or find a number in an image? – Mark Setchell Sep 26 '20 at 11:41
  • @MarkSetchell I got the list from a json file (shown up top - tho w/ different numbers). They represent an image of a Japanese character that is used instead of a text character (think how games work). All I'm trying to do right now is convert those numbers into the image they represent. I believe the individual numbers are bytes and, based on the "bitmap" key, that together they represent a bitmap image. I want to reconstruct that image and either save it to disk or convert it to a data format that tesseract can work with. Isn't this the same thing the posts I linked are doing? – weirdalsuperfan Sep 26 '20 at 12:17
  • You should put all the relevant details in your question, not in comments. So there are 32 numbers that somehow make an image of some shape we don't know (8x4?) that is a Japanese symbol we can't see? – Mark Setchell Sep 26 '20 at 12:22
  • @MarkSetchell The lists (there are actually a lot of these lists) constitute a mapping for a special image-based font used by a Japanese dictionary. Are you saying I should update my question based on these comments? Because I thought I put all that information in already. Yes, I don't know the shape. I think it might be 8 tall by 4 wide. – weirdalsuperfan Sep 26 '20 at 12:23
  • @MarkSetchell I updated my question based on these comments – weirdalsuperfan Sep 26 '20 at 12:29
  • @MarkSetchell And yes, we can't see the symbol, although I have some ground-truth examples I can use to test the output, and I can also just confirm myself if the output is a real Japanese character. – weirdalsuperfan Sep 26 '20 at 12:32

2 Answers2

4

As I have no idea what I am looking for, all I can say is that this is what you get if you assume the data makes an 8x4 or a 4x8 image:

import numpy as np
from PIL import Image

# Make Numpy array from list
na = np.array(
    [
        0, 176, 16, 164, 27, 254, 10, 164,
        75, 252, 58, 164, 27, 252, 16, 4,
        23, 254, 19, 252, 98, 12, 35, 252,
        17, 16, 16, 160, 55, 254, 0, 0
    ], 
    dtype=np.uint8)

# Make PIL Image from numpy array
im = Image.fromarray(na.reshape(4,8))
# or im = Image.fromarray(na.reshape(8,4))

# Save
im.save('result.png')

enter image description here

enter image description here

CivFan
  • 13,560
  • 9
  • 41
  • 58
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • thanks!!! The fact that it's working at all is a huge relief. I added an example image/updated the post with the numbers corresponding to that image. I realized that the image is not greyscale, and is also not 4x8 (it's 16x16 pixels), so I'm not totally sure what these numbers indicate anymore. I'm not working on this on my own so I'll have to get back to you if I can figure out what they really represent, but in the meantime, based on what I uploaded, do you happen to have any idea how these numbers can represent positions or something? – weirdalsuperfan Sep 26 '20 at 15:03
  • 1
    I realized the lists are actually bytes that give the activated pixels from left to right in a 16x16 (i.e. 256 pixels/256 bit) image. That's why there are 32 "bytes" (32x8 = 256). I confirmed that the bytes exactly correspond to the pixel locations. So in my post (I changed the numbers after you answered), 0,0 corresponds to the first row: 0000000000000000. And then 26,0 is the second row: 0001101000000000, and 17,252 is 0001000111111100. Also, instead of a png could you show me how to save a .bmp file? – weirdalsuperfan Sep 26 '20 at 20:09
2

It seems like you're converting the json output of https://github.com/FooSoft/zero-epwing.

I haven't been able to figure out how to convert the wide glyphs, but I used this (absolute hack of a) Python script to export the narrow glyphs.

Change font.json to the path for your json file. It exports bmp's with the glyph code as the filename.

from PIL import Image
import json

with open('font.json') as json_file:
    data = json.load(json_file)
    for font in data['fonts']:
        for glyph in font['narrow']['glyphs']:
            bitmap = glyph['bitmap']
            row = 0
            img = Image.new('1', (24, 24))
            pixels = img.load()
            for high,low in zip(bitmap[::2], bitmap[1::2]):
                bits = list(map(int, list('{:08b}'.format(high) + '{:08b}'.format(low))))
                col = 0
                for bit in bits:
                    pixels[col, row] = not bit
                    col += 1
                row += 1
            img.save(f'{glyph["code"]}.bmp')

This is what the exported glyphs look like.
Hopefully this is enough to get you started.

Exported Glyphs

landonepps
  • 593
  • 4
  • 9
  • Thanks!!! I had figured out a more rudimentary/partially manual method based on my second comment on the other answer, which I'm pretty sure worked for the wide characters too (though it was a year ago so I don't remember the details), but combining that with this looks like it'll do the trick of fully automating everything! I'll post an update if I can put something together that does everything at once. – weirdalsuperfan Oct 13 '21 at 05:35