Commit bbdedeca authored by Rick Gruber-Riemer's avatar Rick Gruber-Riemer

* Make sure update operation for texture atlas can be chosen (not working yet)

* Specify hard-coded dimensions for atlas
* Have an atlas suffix as text instead of date
parent 08dc791f
......@@ -674,7 +674,7 @@ def process(coords_transform: coordinates.Transformation, fg_elev: utilities.FGE
logging.info("No buildings after overlap check etc. Stopping further processing.")
return
prepare_textures.init(stats, False)
prepare_textures.init(stats)
the_buildings = building_lib.analyse(the_buildings, fg_elev,
prepare_textures.facades, prepare_textures.roofs, stats)
......@@ -766,14 +766,12 @@ if __name__ == "__main__":
args = parser.parse_args()
parameters.read_from_file(args.filename)
parameters.set_loglevel(args.loglevel) # -- must go after reading params file
if args.skip_elev:
parameters.NO_ELEV = True
parameters.show()
if args.skip_elev:
parameters.NO_ELEV = True
if args.skip_overlap_check:
parameters.OVERLAP_CHECK = False
parameters.show()
my_coords_transform = coordinates.Transformation(parameters.get_center_global())
my_fg_elev = utilities.FGElev(my_coords_transform)
......
......@@ -195,7 +195,8 @@ Textures
============================================= ======== ======= ==============================================================================
Parameter Type Default Description / Example
============================================= ======== ======= ==============================================================================
ATLAS_SUFFIX_DATE Boolean False Add the current date as a suffix to the texture atlas in ``osm2city-data``.
ATLAS_SUFFIX String (empty) Add the the suffix to the texture atlas (also light-map) in ``osm2city-data``
including an underscore (e.g. 'foo' leads to atlas_facades_foo.png).
TEXTURES_ROOFS_NAME_EXCLUDE List [] List of roof file names to exclude, e.g. ["roof_red3.png", "roof_orange.png"].
The file names must be relative paths to the ``tex.src`` directory within
......
......@@ -252,7 +252,7 @@ BUILT_UP_AREA_LIT_BUFFER = 20 # the buffer around built-up land-use areas to be
# PARAMETERS RELATED TO TEXTURES
# =============================================================================
ATLAS_SUFFIX_DATE = False # -- add timestamp to file name
ATLAS_SUFFIX = '' # -- add a suffix to the atlas/atlas_LM file name
TEXTURES_ROOFS_NAME_EXCLUDE = [] # list of roof file names to exclude, e.g. ["roof_red3.png", "roof_orange.png"]
TEXTURES_FACADES_NAME_EXCLUDE = [] # e.g. ["de/commercial/facade_modern_21x42m.jpg"]
TEXTURES_ROOFS_PROVIDE_EXCLUDE = [] # list of roof provides features to exclude, e.g. ["colour:red"]
......
......@@ -16,9 +16,8 @@ Created on Wed Mar 13 22:22:05 2013
import argparse
import datetime
import enum
import logging
import math
import os
import pickle
import sys
......@@ -37,12 +36,16 @@ atlas_file_name = None
ROOFS_DEFAULT_FILE_NAME = "roofs_default.py"
def _next_pow2(value):
return 2**(int(math.log(value) / math.log(2)) + 1)
# Hard-coded constants for the texture atlas. If they are changed, then maybe all sceneries in Terrasync need
# to be recreated -> therefore not configurable. Numbers are in pixels (need to be factor 2).
ATLAS_ROOFS_START = 0
ATLAS_FACADES_START = 16384
ATLAS_SPECIAL_START = 65536
ATLAS_HEIGHT = 131072
ATLAS_WIDTH = 256
def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: str, size_x: int=256, pad_y: int=0,
lightmap: bool=False, ambient_occlusion: bool=False):
def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: str, pad_y: int=0):
"""
Create texture atlas from all textures. Update all our item coordinates.
"""
......@@ -52,7 +55,6 @@ def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: s
logging.error('Got an empty texture list. Check installation of tex.src/ folder!')
sys.exit(-1)
atlas_sx = size_x
keep_aspect = True # FIXME: False won't work -- im.thumbnail seems to keep aspect no matter what
next_y = 0
......@@ -65,29 +67,28 @@ def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: s
filename = l.filename
l.im = Image.open(filename)
logging.debug("name %s size " % filename + str(l.im.size))
if lightmap:
filename, file_extension = os.path.splitext(l.filename)
filename += '_LM' + file_extension
try:
l.im_LM = Image.open(filename)
except IOError:
l.im_LM = None
# light-map
filename, file_extension = os.path.splitext(l.filename)
filename += '_LM' + file_extension
try:
l.im_LM = Image.open(filename)
except IOError:
l.im_LM = None
assert (l.v_can_repeat + l.h_can_repeat < 2)
if l.v_can_repeat:
l.rotated = True
l.im = l.im.transpose(Image.ROTATE_270)
if lightmap:
l.im_LM = l.im_LM.transpose(Image.ROTATE_270)
l.im_LM = l.im_LM.transpose(Image.ROTATE_270)
if l.v_can_repeat or l.h_can_repeat:
can_repeat_list.append(l)
else:
non_repeat_list.append(l)
# FIXME: maybe auto-calc x-size here
the_atlas = atlas.Atlas(0, 0, atlas_sx, 1e10, 'Facades')
the_atlas = atlas.Atlas(0, 0, ATLAS_WIDTH, ATLAS_HEIGHT, 'Facades')
# Work on not repeatable textures
# Sort textures by perimeter size in non-increasing order
......@@ -103,7 +104,7 @@ def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: s
# Scale each to full atlas width
# Compute total height of repeatable section
for l in can_repeat_list:
scale_x = 1. * atlas_sx / l.im.size[0]
scale_x = 1. * ATLAS_WIDTH / l.im.size[0]
if keep_aspect:
scale_y = scale_x
else:
......@@ -113,16 +114,16 @@ def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: s
nx = int(org_size[0] * scale_x)
ny = int(org_size[1] * scale_y)
l.im = l.im.resize((nx, ny), Image.ANTIALIAS)
if lightmap and l.im_LM:
if l.im_LM:
l.im_LM = l.im_LM.resize((nx, ny), Image.ANTIALIAS)
logging.debug("scale:" + str(org_size) + str(l.im.size))
atlas_sy += l.im.size[1] + pad_y
l.width_px, l.height_px = l.im.size
# Bake fake ambient occlusion. Multiply all channels of a facade texture by
# 1. - parameters.BUILDING_FAKE_AMBIENT_OCCLUSION_VALUE * np.exp(-z / parameters.BUILDING_FAKE_AMBIENT_OCCLUSION_HEIGHT)
# 1. - param.BUILDING_FAKE_AMBIENT_OCCLUSION_VALUE * np.exp(-z / param.BUILDING_FAKE_AMBIENT_OCCLUSION_HEIGHT)
# where z is height above ground.
if ambient_occlusion:
if parameters.BUILDING_FAKE_AMBIENT_OCCLUSION:
for l in texture_list:
if l.cls == 'facade':
R, G, B, A = img2np.img2RGBA(l.im)
......@@ -136,7 +137,7 @@ def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: s
# lower left corner of texture is x0, y0
for l in can_repeat_list:
l.x0 = 0
l.x1 = float(l.width_px) / atlas_sx
l.x1 = float(l.width_px) / ATLAS_WIDTH
l.y1 = 1 - float(next_y) / atlas_sy
l.y0 = 1 - float(next_y + l.height_px) / atlas_sy
l.sy = float(l.height_px) / atlas_sy
......@@ -146,24 +147,19 @@ def _make_texture_atlas(texture_list: List[Texture], atlas_filename: str, ext: s
if not the_atlas.pack(l):
logging.info("Failed to pack" + str(l))
atlas_sy = _next_pow2(atlas_sy)
the_atlas.set_height(atlas_sy)
logging.info("Final atlas height %i" % atlas_sy)
the_atlas.compute_nondim_tex_coords()
the_atlas.write(atlas_filename + ext, 'im')
# -- create LM atlas, using the coordinates of the main atlas
if lightmap:
light_map_atlas = atlas.Atlas(0, 0, atlas_sx, the_atlas.height_px, 'FacadesLM')
for l in texture_list:
light_map_atlas.pack_at(l, l.ax, l.ay)
light_map_atlas.write(atlas_filename + '_LM' + ext, 'im_LM')
light_map_atlas = atlas.Atlas(0, 0, ATLAS_WIDTH, ATLAS_HEIGHT, 'FacadesLM')
for l in texture_list:
light_map_atlas.pack_at(l, l.ax, l.ay)
light_map_atlas.write(atlas_filename + '_LM' + ext, 'im_LM')
for l in texture_list:
logging.debug('%s (%4.2f, %4.2f) (%4.2f, %4.2f)' % (l.filename, l.x0, l.y0, l.x1, l.y1))
del l.im
if lightmap:
del l.im_LM
del l.im_LM
def _check_missed_input_textures(tex_prefix: str, registered_textures: List[Texture]) -> None:
......@@ -185,7 +181,7 @@ def _check_missed_input_textures(tex_prefix: str, registered_textures: List[Text
logging.warning("Texture %s has not been registered", my_path)
def _append_dynamic(facade_manager: FacadeManager, tex_prefix: str) -> None:
def _append_facades(facade_manager: FacadeManager, tex_prefix: str) -> None:
"""Dynamically runs .py files in tex.src and sub-directories to add facades.
For roofs see add_roofs(roofs)"""
......@@ -243,8 +239,16 @@ def _dump_all_provides_across_textures(texture_list: List[Texture]) -> None:
logging.debug("4th level provides: %s", provided_features_level_four)
def init(stats: util.Stats, create_atlas: bool=True) -> None:
logging.debug("textures: init")
@enum.unique
class InitMode(enum.IntEnum):
read = 0
create = 1 # create new texture atlas by reading from sources
update = 2 # update existing texture atlas by reading from sources
def init(stats: util.Stats, mode: InitMode=InitMode.read) -> None:
"""Initializes the texture atlas based on the init mode of the process."""
logging.debug("prepare_textures: init")
global facades
global roofs
global atlas_file_name
......@@ -255,13 +259,13 @@ def init(stats: util.Stats, create_atlas: bool=True) -> None:
pkl_file_name = os.path.join(parameters.PATH_TO_OSM2CITY_DATA, "tex", "atlas_facades.pkl")
if create_atlas:
if mode is InitMode.create:
facades = FacadeManager('facade', stats)
roofs = RoofManager('roof', stats)
# read registrations
_append_roofs(roofs, my_tex_prefix_src)
_append_dynamic(facades, my_tex_prefix_src)
_append_facades(facades, my_tex_prefix_src)
texture_list = facades.get_list() + roofs.get_list()
......@@ -269,12 +273,10 @@ def init(stats: util.Stats, create_atlas: bool=True) -> None:
_check_missed_input_textures(my_tex_prefix_src, texture_list)
# -- make texture atlas
if parameters.ATLAS_SUFFIX_DATE:
now = datetime.datetime.now()
atlas_file_name += "_%04i%02i%02i" % (now.year, now.month, now.day)
if parameters.ATLAS_SUFFIX:
atlas_file_name += '_' + parameters.ATLAS_SUFFIX
_make_texture_atlas(texture_list, os.path.join(parameters.PATH_TO_OSM2CITY_DATA, atlas_file_name), '.png',
lightmap=True, ambient_occlusion=parameters.BUILDING_FAKE_AMBIENT_OCCLUSION)
_make_texture_atlas(texture_list, os.path.join(parameters.PATH_TO_OSM2CITY_DATA, atlas_file_name), '.png')
params = dict()
params['atlas_file_name'] = atlas_file_name
......@@ -309,11 +311,18 @@ if __name__ == "__main__":
parser.add_argument("-l", "--loglevel",
help="set loglevel. Valid levels are VERBOSE, DEBUG, INFO, WARNING, ERROR, CRITICAL",
required=False)
parser.add_argument("-u", "--update", dest="update", action="store_true",
help="update texture atlas instead of creating new", required=False)
args = parser.parse_args()
if args.filename is not None:
parameters.read_from_file(args.filename)
parameters.set_loglevel(args.loglevel) # -- must go after reading params file
parameters.show()
init_mode = InitMode.create
if args.update:
init_mode = InitMode.update
my_stats = util.Stats()
init(my_stats)
init(my_stats, init_mode)
......@@ -8,19 +8,19 @@ import textures.texture as tex
class Region(object):
"""Also used as a container for a single image"""
def __init__(self, x, y, width, height):
def __init__(self, x: int, y: int, width: int, height) -> None:
self.x = x
self.y = y
self.width_px = width
self.height_px = height
logging.debug(" New Region " + str(self)) # @UndefinedVariable
logging.debug(" New Region " + str(self))
def __str__(self):
return "(%i x %i + %i + %i)" % (self.width_px, self.height_px, self.x, self.y)
class Atlas(Region):
def __init__(self, x: int, y: int, width: int, height:float, name: str) -> None:
def __init__(self, x: int, y: int, width: int, height: int, name: str) -> None:
super().__init__(x, y, width, height)
self.regions = [Region(x, y, width, height)]
self._textures = [] # Type atlas.Texture
......@@ -32,10 +32,6 @@ class Atlas(Region):
"""return the current height"""
return self.regions[-1].y
def set_height(self, height):
self.height_px = height
self._compute_nondim_tex_coords()
def write(self, filename, image_var):
"""Allocate memory for the actual atlas image, paste images, write to disk.
image_var is the name (string) of the class variable that stores the image;
......@@ -70,7 +66,7 @@ class Atlas(Region):
the_texture.ay = y
self._textures.append(the_texture)
def _compute_nondim_tex_coords(self):
def compute_nondim_tex_coords(self):
"""compute non-dim texture coords"""
for t in self._textures:
t.x0 = float(t.ax) / self.width_px
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment