|
import os |
|
import argparse |
|
import cv2 |
|
import numpy as np |
|
from tqdm import tqdm |
|
from math import ceil, sqrt |
|
|
|
def atlasFromFolder( name, src, dst, tile, packChannels, quality ): |
|
|
|
if src[:-1] != "/": |
|
src += "/" |
|
|
|
if dst[:-1] != "/": |
|
dst += "/" |
|
|
|
os.makedirs(dst, exist_ok=True) |
|
files = os.listdir(src) |
|
count = 0 |
|
for f in files: |
|
n,e = os.path.splitext( f ) |
|
if n == ".DS_Store" or e == ".DS_Store": #pesky macs |
|
continue |
|
count += 1 |
|
|
|
channels = 1 |
|
if packChannels: |
|
channels = 3 |
|
|
|
print( "files", count ) |
|
perBoard = ceil(count / channels) |
|
size = tile * ceil( sqrt(perBoard ) ) |
|
columns = int( size / tile ) |
|
|
|
print( "image size", size, ", per board", perBoard, "tile", tile ) |
|
print( "grid width", columns ) |
|
print( len( files ), columns ** 2 * channels ) |
|
print( '----------' ) |
|
|
|
# creates separate frames for the R,G,B channels |
|
frames = [] |
|
for i in range(channels): |
|
if channels == 1: |
|
img = np.zeros( (size, size, 3 ) ) |
|
else: |
|
img = np.zeros( (size, size, 1 ) ) |
|
frames.append(img) |
|
frameId = len( frames )- 1 |
|
|
|
# meta |
|
widths = [] |
|
index = 0 |
|
iterator = 0 |
|
for f in tqdm( files ): |
|
n,e = os.path.splitext( f ) |
|
if n == ".DS_Store" or e == ".DS_Store": #pesky macs... |
|
continue |
|
|
|
# texture overflow: too many images, not enough space |
|
if iterator >= columns * columns * channels: |
|
print( 'texture overflow') |
|
break |
|
iterator += 1 |
|
|
|
# open and resize image |
|
tmp = cv2.imread( src + f, cv2.IMREAD_UNCHANGED ) |
|
|
|
# store the width (to rescale instances) |
|
iw = tmp.shape[1] / tmp.shape[0] |
|
widths.append(str( iw )) |
|
|
|
# and resize image |
|
tmp = cv2.resize(tmp, (tile,tile) ) |
|
|
|
# convert to greyscale if packing into R,G,B channels |
|
if channels == 3: |
|
if len( tmp.shape ) != 2: |
|
tmp = cv2.cvtColor(tmp, cv2.COLOR_BGR2GRAY ) |
|
tmp = tmp.reshape((tile,tile, 1)) |
|
|
|
# change frame on overflow |
|
if index >= columns * columns: |
|
print( index, "change frame") |
|
frameId -= 1 |
|
index = 0 |
|
|
|
# find where to paste image |
|
x = ( index % columns ) * tile |
|
y = int(index / columns ) * tile |
|
|
|
# # pick frame and paste |
|
frame = frames[frameId] |
|
frame[ y:(y+tile), x:(x+tile), : ] = tmp |
|
|
|
index += 1 |
|
|
|
# save metadata |
|
# pack frames and save |
|
if channels == 3: |
|
img = cv2.merge(frames, img ) |
|
|
|
# save the atlas |
|
cv2.imwrite( dst + name + ".jpg", img, [int(cv2.IMWRITE_JPEG_QUALITY), quality] ) |
|
|
|
# save images widths |
|
f = open( dst + name + ".txt", "w" ) |
|
f.writelines(','.join( widths )) |
|
f.close() |
|
|
|
parser = argparse.ArgumentParser(description='build a texture atlas from a folder of images.') |
|
parser.add_argument('-name', '-n', type=str, default="atlas", help='atlas name') |
|
parser.add_argument('-source', '-src', type=str, required=True, help='source folder') |
|
parser.add_argument('-destination', '-dst', type=str, default="atlas/", help='destination folder') |
|
parser.add_argument('-size', '-s', type=int, default=64, help='tile size') |
|
parser.add_argument('-pack', '-p', type=bool, default=False, help='pack rgb channels') |
|
parser.add_argument('-quality', '-q', type=int, default=80, help='JPEG quality') |
|
|
|
if __name__ == "__main__": |
|
|
|
args = parser.parse_args() |
|
name = args.name |
|
srcFolder = args.source |
|
dstFolder = args.destination |
|
size = args.size |
|
pack = args.pack |
|
quality = args.quality |
|
|
|
atlasFromFolder( name, srcFolder, dstFolder, size, pack, quality ) |