Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

question: how do i split a tilesheet into multiple grid and merge them together? #357

Open
scarf005 opened this issue Oct 22, 2022 · 5 comments

Comments

@scarf005
Copy link

I need to work with a tilesheet, composed of 10x10 tiles.
for example, a 160x2560 image would contain 16x256=4096 tiles.
after that, I need to merge those 4096 10x10 images back into 160x2560 image.
I tried this:

#!/usr/bin/python3

from pathlib import Path
from pyvips import Image, Region

base = Path("ASCIITileset")
tiles = base / "ASCIITiles.png"
fallback = base / "fallback.png"
config = base / "tile_config.json"

image = Image.new_from_file(fallback, memory=True)  # type: ignore


def fetch(region: Region, x: int, y: int, size: int):
    return region.fetch(size * x, size * y, size, size)


patch_size = 10
width: int = image.width
height: int = image.height
rows, cols = height // patch_size, width // patch_size


region = Region.new(image)
print(f"{width=}, {height=}, {patch_size=}, {rows=}, {cols=}")
# res = image.grid(patch_size, rows, cols)
print(rows, cols)
Path("patches").mkdir(exist_ok=True)

for y in range(rows):
    for x in range(cols):
        print(f"({x}, {y})")
        patch = fetch(region, x, y, patch_size)
        img = Image.new_from_buffer(patch, "")
        img.write_to_file(f"patches/{y}-{x}.png")

but encountered with:

width=160, height=2560, patch_size=10, rows=256, cols=16
256 16
(0, 0)
Traceback (most recent call last):
  File "/home/scarf/repo/cata/tileset-tools/learnvips.py", line 43, in <module>
    img = Image.new_from_buffer(patch, "")
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/scarf/.asdf/installs/python/3.11.0rc2/lib/python3.11/site-packages/pyvips/vimage.py", line 384, in new_from_buffer
    pointer = vips_lib.vips_foreign_find_load_buffer(data, len(data))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: initializer for ctype 'void *' must be a cdata pointer, not _cffi_backend.buffer
@jcupitt
Copy link
Member

jcupitt commented Oct 22, 2022

Hi @scarf005,

You can use dzsave to split an image up, and arrayjoin to reassemble it. For example:

$ vips dzsave ../astronauts.png x --depth one --tile-size 10 --overlap 0 
$ ls x_files/0 | wc
   8100    8100   87300

It made 8100 jpeg tiles (90 x 90). I can reassemble with:

$ cd x_files/0/
$ vips arrayjoin "$(ls *.jpeg | sort -t_ -k2g -k1g)" ../x.jpg --across 90

@scarf005
Copy link
Author

scarf005 commented Oct 22, 2022

thank you, but would there be a way to do that using pyvips api? i need to use them in python script i'm writing.

@jcupitt
Copy link
Member

jcupitt commented Oct 22, 2022

Sure, it's the same. Eg. (untested):

image.dzsave("x", depth="one", tile_size=10, overlap=0)

And:

tiles = [pyvips.Image.new_from_file(f"{x}_{y}.jpeg") for y in range(down) for x in range(across)]
image = pyvips.Image.arrayjoin(tiles, across=90)

(I might have the x and y loops swapped, I always forget which way py list comprehensions do nesting)

@scarf005
Copy link
Author

scarf005 commented Oct 22, 2022

Thank you, it works. however I've encountered thse problems:

image

  1. can't remove _files/0 part.
    for example if i run image.dzsave(Path("patch"), ...), it saves tiles in patch_files/0/.

could there be a way to save images at just patch/?

  1. custom formatting for tilenames?
    for example, it's 0_0.png, 0_1.png, and so on. would there be a way to format them? or should i manually rename after file generation? the reasoning is that i have a large list of names mapped into specific tileset index, for example
        { "id": ["player_female", "player_male"], "fg": 144, "rotates": false },
        { "id": ["t_open_air", "t_open_air_rooved"], "bg": 811 },
        { "id": "lighting_hidden", "fg": 1067, "bg": 1067, "rotates": false },

fg and bg points at specific tile location. I'll have to rename the resulting filename accordingly.

  1. can it also horizontally split images?
    for example, let's day I have 120 * 500 pixel images. can i split it into 5 120 * 100 images, or would there be another method to split them?

  2. save the splitted tiles into memory?
    it would help a lot since my SSD is wearing down fast.

also, your example is correct since (...) for y in (...) for x in (...) is same as

for y in (...):
	for x in (...):
		(...)

for example:

In [3]: [(y, x) for y in range(2) for x in 'ab']
Out[3]: [(0, 'a'), 
         (0, 'b'), 
         (1, 'a'), 
         (1, 'b')]

@jcupitt
Copy link
Member

jcupitt commented Oct 22, 2022

It supports deepzoom, zoomify and gmaps naming convention, so z/x_y, nnn/z_x_y and z/y/x, but that's it. You'll need to run a second pass to rename the files.

It can write an uncompressed zip, which will save a lot of time and disc space, especially on windows. That might be worth checking. You can write to a zip in memory and then use any standard python zip handler to pull tiles out efficiently.

Square tiles only, sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants