Copy link to clipboard
Copied
Hello everyone,
I need some help with this script. This script was written by Paul Riggott I believe and posted on the forums. It stitches together tiles of images. I've touched a few lines so it suits my needs (deleted mostly ). It works but I've hit a bump. The output tiles are numbered 1, 2, 3, ..., 10, 11, ..., etc. As you can seen in the image attached Photoshop sorts tile number 10 right before tile number 1.
Could you point me to what/how should I change the script code so Photoshop understands the sorting the way I need it? I really am a total newbie at this - first time I've touched PS script (or any other) was this morning so all your advises would be much appreciated
There is the script code I'm using:
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(/\.(jpeg|tif|eps|psd|png)$/i);
var startRulerUnits = preferences.rulerUnits;
preferences.rulerUnits = Units.PIXELS;
var Down = (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*1;
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
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 );
};
pngOptions = new PNGSaveOptions()
pngOptions.compression = 0
pngOptions.interlaced = false
savePath = inputFolder;
activeDocument.saveAs(savePath, pngOptions, false, Extension.LOWERCASE)
Copy link to clipboard
Copied
I’m getting some errors with your script, so I can’t even run it to see what the result is… perhaps you cut too much?
Are the layer names the original filenames, or is the script creating the layer names differently to the input files?
I just hunted down the original thread and will take a look at the original code:
Copy link to clipboard
Copied
Hello Stephen,
The layer names are the original file names - yes. The script doesn't interfere with file names. It just arranges them in sequentially. I think the problem comes from that PS needs a 0 for the digits ( for example - 01, 02, 03, etc.).
I can work around it by splitting the image in 9 tiles if there is no solution to the problem
By the way, the script is running on my side, I don't know why it misbehaves with you.
Copy link to clipboard
Copied
So could you try changing the filenames first?
Copy link to clipboard
Copied
I could, but they are saved like that automatically after rendering. Which means I'll have to rename them manually (or by some other script) and I have like 50 +/- passes x 10 tiles... which would be quite time consuming
Copy link to clipboard
Copied
Adobe Bridge’s Batch Rename tool with regular expression based string substitution excels at such tasks, however we need to find out some sort of pattern to the filenames. If the names are preset by some other software, then there is likely a variable but consistent pattern. It would be best if this other software could write out the files using the zero padding in the first place.
Do you have different examples of filenames?
Would you only need a max of up to 2 digits, i.e. 01, 02…10 ? Or pad out with 001 or 0001 etc?
Example:
Find: (^\w+tile_)(\dx.+)
Replace: $10$2
Scripting could do this in your script in a single pass (either using regular expressions or some other method of file filtering/sorting), however I probably can’t help too much there… so the two pass/step batch rename input files then run your tile script is just an interim suggestion to get the job done until you find a better alternative.
Copy link to clipboard
Copied
Stephen,
Thank you for the input. All file names have the same pattern. I'll try to figure something out with that expression when I have more time.
JJMack,
Thank you for the script, looks really good. Unfortunately it is not what I need for my workflow.
Copy link to clipboard
Copied
Another thought, if you will always have a stack of 10 images and a background image layer, and the incorrect layer is always in the third position from the bottom and needs to go to the top of the stack… Put one of the following at the end of your script to select and move the layer into the correct position.
Option 1: Selecting the back layer, regardless of layer stack quantity (probably the best method compared to option 3?)
//Select back layer
var doc = app.activeDocument;
doc.activeLayer = doc.layers[doc.layers.length -1];
//Select forward layer x2
select(), select(); //Run it x2
function select() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "forwardEnum" ));
descriptor.putReference( c2t( "null" ), reference );
descriptor.putBoolean( s2t( "makeVisible" ), false );
executeAction( s2t( "select" ), descriptor, DialogModes.NO );
}
//move selected layer to front
move();
function move() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "targetEnum" ));
descriptor.putReference( c2t( "null" ), reference );
reference2.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "front" ));
descriptor.putReference( s2t( "to" ), reference2 );
executeAction( s2t( "move" ), descriptor, DialogModes.NO );
}
Option 2: Selecting the _10x1_ layer by absolute layer order in a layer stack of 11
//Select the 3rd layer from the bottom of 11 layers
var doc = app.activeDocument;
doc.activeLayer = doc.layers[8];
//move selected layer to front
move();
function move() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "targetEnum" ));
descriptor.putReference( c2t( "null" ), reference );
reference2.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "front" ));
descriptor.putReference( s2t( "to" ), reference2 );
executeAction( s2t( "move" ), descriptor, DialogModes.NO );
}
Option 3: Selecting the _10x1_ layer by it’s position to a layer named “Background” (whether a true Background image layer or standard layer)
//Select layer named "Background"
var doc = app.activeDocument;
doc.activeLayer = doc.layers.getByName ("Background");
//Select forward layer x2
select(), select(); //Run it x2
function select() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "forwardEnum" ));
descriptor.putReference( c2t( "null" ), reference );
descriptor.putBoolean( s2t( "makeVisible" ), false );
executeAction( s2t( "select" ), descriptor, DialogModes.NO );
}
//Move selected layer to front
move();
function move() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "targetEnum" ));
descriptor.putReference( c2t( "null" ), reference );
reference2.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "front" ));
descriptor.putReference( s2t( "to" ), reference2 );
executeAction( s2t( "move" ), descriptor, DialogModes.NO );
}
P.S. I’m sure that there are better ways, I’m just a scripting newb, so I am happy to learn from my mistakes!
EDIT: I think that this may be the better option so far!
//Select back layer
var doc = app.activeDocument;
doc.activeLayer = doc.layers[doc.layers.length -1];
//Select forward layer x2
select(), select(); //Run it x2
function select() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "forwardEnum" ));
descriptor.putReference( c2t( "null" ), reference );
descriptor.putBoolean( s2t( "makeVisible" ), false );
executeAction( s2t( "select" ), descriptor, DialogModes.NO );
}
//move selected layer to front
doc.activeLayer.move(doc.layers[0], ElementPlacement.PLACEBEFORE);
Copy link to clipboard
Copied
Copy link to clipboard
Copied
It look like you are trying to tile images. To do the evenly Images need be fitted to a tile size and then tile a canvas that the tiles will fill perfectly., There are a couple of potential problem tiling Image that do not have an aspect ration close to the tile aspect ratio when you want the tile surface filled with image content. And there is an additional problem if you want to use PNG images that have transparent boarders and the boarders are important for you to have borders around your object subject For both Place and Paste will trim transparent boarder. Images when fitted to a tile size will have a tile image content that looks like a centered aspect ratio crop of your image resized for the tile size. The problem is Portrait to Landscape and Landscape to Portrait to Landscape centered crops image content in most cases will not be acceptable. It easy to eliminate Transparent Png Boarder problems by first eliminating these boarders by putting a 1% opacity pixel in the canvas top left and bottom right corners. No Human eye will see these pixels the image will look like it has transparent borders.
In my Photo Collage Toolkit there is a script the you most likely use to tile your image the way you want to. You set up the paper width and tile size then select the image you want to tile. They need to be in a single folder but you need to select the Image files you want tiled from that folder. The script's name is PasteImageRoll. About the Script and the PasteImageRoll.jsx
The script just created the tiled document. Your images are fitted to the tiles you can tweak the tile images content using Photoshot to transform the masked image layer content
Copy link to clipboard
Copied
You probably already solved the issue. But i had made a grid image stitcher couple years back. It could do column by column or row by row. I was making a grid cropper last week for some social design template i got. So i went on and tested my old stitcher and seemed the row by row had some issues. My idea was to perhaps combine these scripts into 1, i dropped that idea. But i do have them now combined in the script menu in their own tab, which is nice.
Both scripts allow you to now do either column by column or row by row. hahah tooks me many hours this weekend to get it properly working. I had taken some scripts from a Blender addon which does row by row, my earlier grid cropper only did column by column.
Mine works by sorting the images, it does that by the last part of the file name. Something what got me going nuts was the row by row naming. Somehow it was working correct, but because for both methods i use NAME + pos_col + pos_row the export showed a wrong order, it would always show as if it was export as column by column.
Currently, I have this delay in the script, so the use first picks the folder, then loads the images and sets orientation, then we choose the ordering. But i think all these settings can be done from the start. I think i used a couple parts of the stack-loader script and adjusted it. My first concept needed manual input of the tile-size to figure out the document size.
But i figured out a new approach and now i can skip that step. As the user now sets the orientation, it will figure out the correct columns and rows and does sizing automatically.
Mine is not directed as that one of JJMack, his seems more towards print and scales everything to the document size. Mine was created of the need for my 3d work i also do. I need to be able to stitch up big rendered tiles of scenes i render.
I do need to finish the panel for the cropper, that currently only does the orientation by altering the script. I will be same panel as the stitcher basically. I also want to implement the icons into the script. Currently they are separate small images aside of the script. But i want to make that cleaner.
Copy link to clipboard
Copied
PS thanks for pointing out to that script, i already have his scripts collection from github. I noticed he used the place command. I implemented that now i my version as well. That was a huge speed up. It went from 36.8 seconds to 16.4 seconds for 15 images. Thats half the time, huge speedup 🙂 nice!!!
Copy link to clipboard
Copied
Placing or replacing / linking Smart object give you fastest acceleration.
Copy link to clipboard
Copied
@schroef – If this is not a commercial project, then please do share the script code when you are happy with them, from the preview images above they look very useful.