Most people working with 3D modeling software are not aware of how heavy textures can impact performance in the modeling process and the end-user experience.
When generating textures I always recommend creating them at a higher resolution than the final product. The reasoning is that you can always down-sample the image (Reduce number of pixels) later on but you can never scale the image back up.
For procedural modeling in Esri CityEngine and generating your libraries optimizing texture and model asset size as much as possible is key for ensuring fast performance and the ability to model massive environments.
The following script takes the difficulty out of manually scaling the images in your pipeline enabling complete automation allowing you to continue focusing on the design aspect of your project you know and love...
# -------------------------------------------------------------------------------
# Name: ImageAutoResize.py
# Purpose: Script for batch optimizing image for optimal performance in 3D software
#
# Author: Geoff Taylor
#
# Created: 11/19/2018
# Copyright: (c) Esri 2018
# updated:
# Required:
# License: Apache
# -------------------------------------------------------------------------------
import os, sys
from math import ceil
from PIL import Image, ImageStat
inFolder = r'inputFolderPathGoesHere!'
outFolder = r'outputFolderPathGoesHere!'
''' Max Width and height are only relative for a single side of the image.
if one side of the image is larger then the smaller side will be scaled to the minimum value
and larger scaled fractionally to the smaller side'''
minWidth = 300
minHeight = 300
fileFormats = ['.jpg'] # Currently only Supports .jpg
###############
# Definitions #
###############
def update_progress(progress):
barLength = 10 # Modify this to change the length of the progress bar
status = ""
if isinstance(progress, int):
progress = float(progress)
if not isinstance(progress, float):
progress = 0
status = "error: progress var must be float"
if progress < 0:
progress = 0
status = "Halt..."
if progress >= 1:
progress = 1
status = "Done..."
block = int(round(barLength*progress))
text = "\rPercent Complete: [{0}] {1}% {2}".format("#"*block + "-"*(barLength-block), progress*100, status)
sys.stdout.write(text)
sys.stdout.flush()
def fileExt(inFile):
''' [0] returns fileName, [1] returns Extension '''
filename, file_extension = os.path.splitext(inFile)
return filename, file_extension.lower()
def genDirectory(inPath):
if not os.path.exists(inPath):
os.makedirs(inPath)
def delIfFileExists(inFile,outFolder):
fullFilePath = os.path.join(outFolder, inFile)
if os.path.exists(fullFilePath):
try:
os.remove(fullFilePath)
except:
print("Could not remove {0}".format(fullFilePath))
else:
pass
def imageColorType(pilImg):
thumb_size = 40
MSE_cutoff = 22
adjust_color_bias = True
bands = pilImg.getbands()
if bands == ('R', 'G', 'B') or bands == ('R', 'G', 'B', 'A'):
thumb = pilImg.resize((thumb_size, thumb_size))
SSE, bias = 0, [0, 0, 0]
if adjust_color_bias:
bias = ImageStat.Stat(thumb).mean[:3]
bias = [b - sum(bias)/3 for b in bias]
for pixel in thumb.getdata():
mu = sum(pixel)/3
SSE += sum((pixel - mu - bias)*(pixel - mu - bias) for i in [0, 1, 2])
MSE = float(SSE)/(thumb_size*thumb_size)
if MSE <= MSE_cutoff:
return "grayscale"
else:
return "color"
elif len(bands) == 1:
return "bw"
else:
return "unknown"
def imageFitsDimensions(inPilImg, minWidth, minHeight):
width, height = inPilImg.size
if width <= minWidth or height < minHeight:
return True
else:
return False
def imageScaling(inPilImg, minWidth, minHeight):
width, height = inPilImg.size
if height > width:
scaleFactor = minWidth / width
return minWidth, ceil(scaleFactor*height)
elif width > height:
scaleFactor = minHeight / height
return ceil(scaleFactor*width), minHeight
else:
return minWidth, minHeight
################
# Begin Script #
################
genDirectory(outFolder)
imageFiles = [f for f in os.listdir(inFolder) if
os.path.isfile(os.path.join(inFolder, f)) and fileExt(f)[1] in fileFormats]
count = 0
for imageFile in imageFiles:
if count > 0:
update_progress(count/len(imageFiles))
delIfFileExists(imageFile, outFolder)
inFile = os.path.join(inFolder, imageFile)
outFile = os.path.join(outFolder, imageFile)
count += 1
try:
im = Image.open(inFile)
if imageColorType(im) != "color":
im.convert('RGB')
if imageFitsDimensions(im, minWidth, minHeight):
im.save(outFile, "JPEG")
else:
imageScaling(im, minWidth, minHeight)
im.save(outFile, "JPEG")
outImg = im.resize((imageScaling(im, minWidth, minHeight)[0], imageScaling(im, minWidth, minHeight)[1]),
Image.ANTIALIAS)
# outImg.save(outFile, quality=95)
outImg.save(outFile, optimize=True, quality=95)
except IOError:
print("cannot resample image for" % imageFile)
update_progress(100)Just install the Python Imaging Library fork "Pillow" https://python-pillow.org/ and run the script on the folder containing your textures.