Skip to main content
Inspiring
September 14, 2009
Answered

Request for Script - Restitching image tiles

  • September 14, 2009
  • 5 replies
  • 20259 views

I originally posted this in the Photoshop Mac forum and it was suggested I post here for some help.

I'm rendering out a large-ish print piece  (7500x5000pixel HDR) in my 3D app, Cinema 4D. It has a feature called Tiled Camera that chops the scene into a number of tiles and sends each one to a network render client. This is considerably faster than one machine doing the entire frame.

The problem is that I have to manually combine all the image tiles in Photoshop once its done. Yeah, it's kinda clunky. The tile camera doesn't have any overlap between adjacent tiles, so Photomerge doesn't have any pixels to line up.


Depending on the project, there might be 9 tiles or there might be 1000. The number of rows and columns always = √"total # of images". All tiles are the same size. The final image length would be (√"total # of images" * "one tile X width in pixels").  Likewise, height would be (√"total # of images" * "one tile Y height in pixels"). The Tile Camera renders across first then down, so it seems like the process would be "easy" to automate.

It would be cool if the script would auto recognize the number of images and calculate the final canvas size using the above math.  But even if a user has to manually input those variables, that would still save hours of hand assembly work.

If anyone feels exceptionally generous and daring, I attached a folder of JPEGs rendered out by the Tile Camera.


Here's how it should look when assembled:

TileCameraGOAL.jpg

I'd really appreciate any time someone could devote to this.

This topic has been closed for replies.
Correct answer Paul Riggott

Geez Paul! I'm using open source libraries to compress these files. (Tried uploading 7z and XAR, but the forums wouldn't allow it) Maybe the problem is on your end? Or the Adobe Forum isn't properly uploading compressed files?

So anywho - let's all assume this is a Mac bug.  How would you supress the color warning within the stitch script?

(I'm going to file a bug report for the Mac version. (Really wish another Mac user would try to open it to confirm))


I have just tried with Photoshop CS3 and Tiger and no prompts/errors.

I wonder if you have tried resetting your preferences?

If that doesn't work you could try adding this line at the top of the script..

app.displayDialogs = DialogModes.NO;

5 replies

Participant
August 25, 2010

Ok  first of all thanks to Navarro for requesting script (which I was prepared to do) and to the Paul Riggot for writing the script.

I have been maintaining the map files for an online game which I have to generate from approx. 1600 terrain directories, each containing 25 128x128 pixel .png images. (I had to change the script to look for .png files in the target directory and i had to change the font Times in the dialog which apparently isn't one the fonts included with Windows version of Photoshop).

I recently upgraded to Windows 7 and the 32-bit program I previously used stopped passing information for opening multple files to an application.

Again Thank you

Participating Frequently
February 15, 2013

Great Script Paul, if you fancy a challenge, I'd like to achieve the same result with a multi-pass render from Cinema 4D.  Basically that means each tile is a PSD which has a load of layers that I'd like to come together in the assembled image.  Do you think that's feasible?

Paul Riggott
Inspiring
February 15, 2013

I am not sure what you mean, as it is psd files are supported and brought in as smart objects, if left as so, you will be able to access all the layers for each tile.

Paul Riggott
Inspiring
September 15, 2009

Here is another version that you could try....

By the way, why is it that zip files are corrupted when downloaded on a windows machine? I had to download on one of my Macs(shudder!).

#target photoshop
function main(){
var dlg=
"dialog{text:'Script Interface',bounds:[100,100,500,280],"+
"panel0:Panel{bounds:[10,10,390,170] , text:'' ,properties:{borderStyle:'etched',su1PanelCoordinates:true},"+
"title:StaticText{bounds:[50,10,320,40] , text:'Tile Camera Maker' ,properties:{scrolling:undefined,multiline:undefined}},"+
"panel1:Panel{bounds:[10,50,370,120] , text:'' ,properties:{borderStyle:'etched',su1PanelCoordinates:true},"+
"folder1:EditText{bounds:[10,10,300,30] , text:'' ,properties:{multiline:false,noecho:false,readonly:false}},"+
"Browse:IconButton{bounds:[310,10,350,30] , icon:'SourceFolderIcon',properties:{style:'button'}},"+
"smart:Checkbox{bounds:[10,40,200,61] , text:'Smart Objects?' },"+
"flatten:Checkbox{bounds:[210,40,380,61] , text:'Flatten File?' }},"+
"process:Button{bounds:[10,130,180,151] , text:'Process' },"+
"button1:Button{bounds:[200,130,370,151] , text:'Cancel' }}};";
var win = new Window(dlg,'Tile Camera Maker');
if(version.substr(0,version.indexOf('.'))>9){
win.panel0.title.graphics.font = ScriptUI.newFont("Times","BOLDITALIC",20);
g = win.graphics;
var myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1.00, 1.00, 1.00, 1]);
g.backgroundColor = myBrush;
var myPen =g.newPen (g.PenType.SOLID_COLOR, [1.00, 0.00, 0.00, 1],lineWidth=1);
}
win.panel0.panel1.folder1.enabled=false;
win.center();
//var inputFolder = Folder.selectDialog("Please select the folder with Files to process");
win.panel0.panel1.Browse.onClick = function() {
inputFolder = Folder.selectDialog("Please select the folder with Files to process");
if(inputFolder !=null){
  win.panel0.panel1.folder1.text =  decodeURI(inputFolder.fsName);
  }
}
win.panel0.process.onClick = function() {
if(win.panel0.panel1.folder1.text == '') {
   alert("No input folder selected!");
   return;
  }
win.close(1);
ProcessFiles();
}
win.show();

function ProcessFiles(){
var fileList = inputFolder.getFiles(/\.(jpg|tif|eps|psd|exr)$/i);
var startRulerUnits = preferences.rulerUnits;
preferences.rulerUnits = Units.PIXELS;
var Down = Math.sqrt(fileList.length);
fileList = fileList.sort();
var Across = Down;
open(fileList[0]);
app.activeDocument.duplicate(fileList[0].name.match(/(.*)\.[^\.]+$/)[1]+"-combined");
app.documents[0].close(SaveOptions.DONOTSAVECHANGES);
var FillColor = new SolidColor;
FillColor.rgb.hexValue = 'ffffff';
activeDocument.selection.selectAll();
activeDocument.selection.fill(FillColor);
activeDocument.selection.deselect();
var w=app.activeDocument.width*Down;
var h=app.activeDocument.height*Down;
var offsetX = app.activeDocument.width.value;
var offsetY = app.activeDocument.height.value;
app.activeDocument.resizeCanvas(w, h, AnchorPosition.TOPLEFT);
TLX = 0; TLY = 0; TRX = offsetX; TRY = 0;
BRX = offsetX; BRY = offsetY; BLX = 0; BLY = offsetY;
var z =0;
for(var a = 0; a < Down; a++){
  for(var i = 0;i <Across; i++){
    activeDocument.selection.select([[TLX,TLY],[TRX,TRY],[BRX,BRY],[BLX,BLY]], SelectionType.REPLACE, 0, false);
    placeFile(fileList);
    if(!win.panel0.panel1.smart.value){
    rasterLayer();
    }
    activeDocument.activeLayer.name = fileList.name.match(/(.*)\.[^\.]+$/)[1];
    app.activeDocument.selection.deselect();
    z++;
TLX = offsetX * (i+1) ; TRX  = TLX + offsetX; BRX = TRX; BLX = TLX; 
    }
TLX = 0; TLY = offsetY * (a +1); TRX = offsetX; TRY = offsetY * (a +1);
BRX = offsetX; BRY = TRY + offsetY; BLX = 0; BLY = (offsetY * (a +1)+offsetY);
}
if(win.panel0.panel1.flatten.value){
activeDocument.flatten();
}
app.preferences.rulerUnits = startRulerUnits;
}
}
main();
function placeFile(placeFile) {
  function cTID(s) { return app.charIDToTypeID(s); };
    var desc21 = new ActionDescriptor();
    desc21.putPath( cTID('null'), new File(placeFile) );
    desc21.putEnumerated( cTID('FTcs'), cTID('QCSt'), cTID('Qcsa') );
        var desc22 = new ActionDescriptor();
        desc22.putUnitDouble( cTID('Hrzn'), cTID('#Pxl'), 0.000000 );
        desc22.putUnitDouble( cTID('Vrtc'), cTID('#Pxl'), 0.000000 );
    desc21.putObject( cTID('Ofst'), cTID('Ofst'), desc22 );
    executeAction( cTID('Plc '), desc21, DialogModes.NO );
};
function rasterLayer() {
    var desc9 = new ActionDescriptor();
        var ref4 = new ActionReference();
        ref4.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
    desc9.putReference( charIDToTypeID('null'), ref4 );
    executeAction( stringIDToTypeID('rasterizeLayer'), desc9, DialogModes.NO );
};

Inspiring
September 15, 2009

Wow Paul, simply incredible!  UI dialog and all... Mr. Fancy!

Sorry about the ZIP. Don't know why it wasn't Windows friendly. They usually are.

Your script works great with HDR EXRs and TIFFs! The only weird thing is that Photoshop acts likes it's frozen until the script is complete.

But your script just stitched together a 5GB image composed of almost 1000 tiles in 10 minutes! Wow.

And PSD CS4 really doesn't like HDR images without color profiles. It keeps bonking me for every image. But I guess that's an issue for another thread.

c.pfaffenbichler
Community Expert
Community Expert
September 16, 2009

Have You tried adjusting Your Color Settings like Michael recommended?

I suppose one could include that in the Script and reset it again, but unless the warning is important to Your work-flow it should be easier simply turning it off.

c.pfaffenbichler
Community Expert
Community Expert
September 15, 2009

You could give this a try:

// tries to assemble fragmented renderings of equal size and strict order;

// 2009, use it at your own risk;

#target photoshop

// selection-dialog;

var theFolder = Folder.selectDialog();

if (theFolder) {

var origUnits = app.preferences.rulerUnits;

app.preferences.rulerUnits = Units.POINTS;

var theFiles = theFolder.getFiles(/\.(jpg|tif|eps|psd)$/i);

theFiles.sort();

var theNumberPerSide = Math.sqrt(theFiles.length);

// open first file;

var theOFile = app.open(File(theFiles[0]));

var theWidth = theOFile.width;

var theHeight = theOFile.height;

theOFile.layers[theOFile.layers.length - 1].isBackgroundLayer = false;

theOFile.resizeCanvas(theOFile.width * theNumberPerSide, theOFile.height * theNumberPerSide, AnchorPosition.TOPLEFT);

var thePos = [0,0];

var horCount = 0;

var verCount = 0;

// transfer layers and offset them;

for (var m = 1; m < theFiles.length; m++) {

var theAddFile = app.open(File(theFiles));

if (theAddFile.layers.length > 1) {

theAddFile.flatten()

};

theAddFile.layers[0].duplicate(theOFile, ElementPlacement.PLACEATBEGINNING);

theAddFile.close(SaveOptions.DONOTSAVECHANGES);

var theLayer = theOFile.layers[0];

theLayer.name = theFiles.name.slice(0,-4)

var horStartPos = theLayer.bounds[0];

var verStartPos = theLayer.bounds[1];

// set the horicontal count;

if (horCount < theNumberPerSide - 1) {

horCount = horCount + 1

}

else {

horCount = 0

};

// set the vertical count;

var verCount = Math.floor(m / theNumberPerSide);

// calculate the offsets;

var horOffset = (horStartPos * (-1)) + (theWidth * horCount);

var verOffset = (verStartPos * (-1)) + (theHeight * verCount);

theLayer.translate(horOffset, verOffset)

};

// save as new file, thanks  to xbytor;

var basename = theOFile.name.match(/(.*)\.[^\.]+$/)[1];

var docPath = theOFile.path;

psdOpts = new PhotoshopSaveOptions();

psdOpts.embedColorProfile = true;

psdOpts.alphaChannels = false;

psdOpts.layers = true;

psdOpts.spotColors = true;

theOFile.saveAs((new File(docPath+'/'+basename.slice(0,-4)+"_comb.psd")),psdOpts,false);

app.preferences.rulerUnits = origUnits;

};

Inspiring
September 15, 2009

Christoph, you are completely amazing!!  Do you have a PayPal tip jar?

I've tested it out with different numbers of cameras, different resolutions, and aspect ratios and it works perfectly! I'm knocked out!

I've run into two speed bumps that might be out of the scope of scripting.

First, if I render HDR OpenEXR files (my favorite format) and try to reassemble them with your script, I get this error. I thought CS4 supported EXR natively.

OpenEXR open error.png

If I render out HDR 32-bit per channel TIFFs or PSDs, I get this missing profile for each image. (I don't get the error with 8-bit or 16-bit images). Is there a way to suppress the warning in the script?

HDR 32bit per channel color error.png

Inspiring
September 15, 2009

I think you need the OpenEXR plugin to open EXR files in Photoshop.

You should be able to suppress the profile dialog by editing your Color Management Policies in the Color Settings preference

Inspiring
September 14, 2009

Let me ask to make sure I understand what you're looking for.

You only need something to calculate the final dimensions of the assembled image file, correct? Or, are you looking for a script that can reassemble the smaller images into the larger image?

Inspiring
September 15, 2009

In the meantime, here is a script that will get you the overall image size information....

var inputFolder=new Folder();
infold=inputFolder.selectDlg("Select Folder for conversion");
var inputFiles = infold.getFiles("*.jpg");
var numf = inputFiles.length;
var row = Math.sqrt(numf);
var imageArray=File(inputFiles[0]);
open(imageArray);
var im=app.activeDocument;
var w=im.width*row;
var h=im.height*row;
im.close(SaveOptions.DONOTSAVECHANGES);
alert(w);
alert(h);

From here, it would be nothing more than a looping routine to stick the pieces into a main image. Especially since you have them numbered in order.