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:
I'd really appreciate any time someone could devote to this.
Request for Script - Restitching image...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?
Request for Script - Restitching image...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.
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 %26lt; theFiles.length; m++) {
var theAddFile = app.open(File(theFiles[m]));
if (theAddFile.layers.length %26gt; 1) {
theAddFile.flatten()
};
theAddFile.layers[0].duplicate(theOFile, ElementPlacement.PLACEATBEGINNING);
theAddFile.close(SaveOptions.DONOTSAVECHANGES);
var theLayer = theOFile.layers[0];
theLayer.name = theFiles[m].name.slice(0,-4)
var horStartPos = theLayer.bounds[0];
var verStartPos = theLayer.bounds[1];
// set the horicontal count;
if (horCount %26lt; 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;
};
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.
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?
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
Yeah, I open EXR files all the time in CS4. So I'm curious why the script is throwing up an error message.
It may be that EXR files need open arguments when scripting like PDF does.
It may also be that the file type is not the issue. The error says the file referenced does not exists. So maybe the issue is the way the file is referenced.
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('.'))%26gt;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]+''-combin ed'');
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 %26lt; Down; a++){
?for(var i = 0;i %26lt;Across; i++){
?activeDocument.selection.select([[TLX,TLY],[TRX,TRY],[BRX,BRY],[BLX,BLY]], SelectionType.REPLACE, 0, false);
?placeFile(fileList[z]);
?if(!win.panel0.panel1.smart.value){
?rasterLayer();
?}
?activeDocument.activeLayer.name = fileList[z].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 );
};
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.
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.
Yeah, I'm familiar with Adobe Color Settings. And I've checked to make sure the settings match yours about missing profiles.
However there's something unique about 32bpc files that makes Photoshop display ''missing profile'' error even though I told it to ignore it. It correctly ignores missing profiles in 8bpc and 16bpc images.?Maybe this is a bug?
Can anyone else confirm?
Try opening this EXR with Photoshop.
(Posting as Gzip TAR because of the funky Mac ZIP errors Windows users were getting)
I've tried opening the exr, and then saving as tif/psd/psb and no prompts/errors at all. That was with CS3 and CS4 using Windows Vista
Oh by the way still have problems Gzip Tar on windows, had to download and unzip it on the Mac.
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;
Resetting seems to have done the trick! Using North America General Purpose 2, I no longer get missing profile messages!
Previously, I was using Monitor Color and Color Management Off... which surprisingly gave me color profile warnings (even when turned off)!
So thanks for all your help! Would you mind if I posted this script to the Cinema 4D forum on CGTalk?
I'm glad you have sorted the problem, and please share the script whereever you feel it will be of use.
Thanks Paul! Oh, one last thing I just thought of. Will this script automatically switch the .PSD to .PSB Large Document format if the reassembly canvas exceed 30,000 pixels?
Also, what would happen if I used multi-layer PSDs? Would it just grab the top most layer?
The script just assembles the tiles so file type just doesn't come into the equation, you can save the file as whatever the filesize/format will allow.
As for multi-layered pdf/tifs it should be fine as the script uses the ''place command'' and the files are brought in as a smart objects, if the smart object checkbox is left un-ticked the smart object is rasterized.
Hey Paul!
I posted the script here and it's gotten very positive reaction!
http://forums.cgsociety.org/showthread.php?f=94%26amp;t=808343
Thanks so much for your help!
No comments:
Post a Comment