/* Adobe Camera Raw Calibrator L v3.0 by Simon Tindemans Adapted from Thomas Fors' ACR-calibrator 3.8 beta With fragments from ACR_Calibrator_Rags V3.8 Original script copyright (c) 2005-2006 Thomas Fors. All rights reserved. Fragments copyright (c) 2005 Rags Gardner. All Rights Reserved. Original copyright and license statements apply as listed below. For usage instructions and a list of changes compared to earlier versions and the original ACR Calibrator script, visit http://www.21stcenturyshoebox.com/tools/ACRcalibrator.html */ // Copyright & license statement from original script: // ======================================================= // Adobe Camera Raw Calibrator // // Copyright (c) 2005 Thomas Fors. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided the following conditions are met: // // 1. Redistribution in source code must contain the above copyright notice, // this list of conditions and the following disclaimer. // 2. Redistribution in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other matereials provided with the distribution. // 3. The name of the author may not be used to promote or endorse products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ======================================================= // ======================================================= // Configuration // ======================================================= var createDebugFiles = false; // defaults for the dialog box var resetSliders = true; var illumType = "D50"; //D50, D65, A var calMethod = "useXMP"; // linearPreset, useXMP, curveFit var calShadowTint = false; var ignoreBrightness = false; var DE_kL = 2.5; var DE_kH = 1.5; var DE_kC = 1; var numberOfPasses = 1; // default patch weights var weights = Array( 3, // dark skin 3, //light skin 3, //blue sky 3, //foliage 2, //blue flower 1, //bluish green 1, //orange 2, //purplish blue 2, //moderate red 1, //purple 3, //yellow green 1, //orange yellow 1, //blue 1, //green 1, //red 1, //yellow 1, //magenta 1, //cyan 0, //white (a bit unreliable, or so it seems) 1, //neutral 8 1, //neutral 6.5 1, //neutral 5 1, //neutral 3.5 0.5 //black ); // ======================================================= // Interface to Adobe Camera Raw // ======================================================= // ------------------------------------------------------- // Acr() // Constructor // ------------------------------------------------------- function Acr() { this.temp = 5000; // Color Temp: 2000 to 50000 this.tint = 0; // Tint: -150 to 150 this.exps = 0; // Exposure: -4.0 to +4.0 this.shad = 5; // Shadows: 0 to 100 this.brgh = 50; // Brightness: 0 to 100 this.cntr = 25; // Contrast: -50 to 100 this.strt = 0; // Saturation: -100 to 100 this.shrp = 0; // Sharpness: 0 to 100 this.lnr = 0; // Luminance Smoothing: 0 to 100 this.cnr = 25; // Color Noise Reduction: 0 to 100 this.ca1 = 0; // Chromatic Aberration (R/C): -100 to 100 this.ca2 = 0; // Chromatic Aberration (B/Y): -100 to 100 this.viga = 0; // Vignetting Amount: -100 to 100 this.vigm = 50; // Vignetting Midpoint: 0 to 100 this.blkb = 0; // Shadow Tint: -100 to 100 this.rhue = 0; // Red Hue: -100 to 100 this.rsat = 0; // Red Saturation: -100 to 100 this.ghue = 0; // Green Hue: -100 to 100 this.gsat = 0; // Green Saturation: -100 to 100 this.bhue = 0; // Blue Hue: -100 to 100 this.bsat = 0; // Blue Saturation: -100 to 100 this.clrs = "ProP"; // Color Space this.btdp = "BD8 "; // Bit Depth this.sz = 1024; // Image Size (width) -- ACR will chose closest available this.rslt = 240; // Resolution this.reun = "PpIn"; // Resolution Units this.nOpens = 0; // Raw file open counter this.nOpenCalls = 0; this.nCloseCalls = 0; this.version = "Unknown"; // ACR Version this.curveSupported = false; this.parametricCurveSupported = false; this.acr41features = false; this.linearCurve = false; // should always remain zero this.fillLight=0; this.vibrance=0; this.recovery=0; this.clarity=0; this.profileVersion = ""; this.curveValues = Array(); } // ------------------------------------------------------- // Initialize() // ------------------------------------------------------- Acr.prototype['Initialize'] = function () { this.SetFileName(this.GetActiveDocFileName()); this.SetCameraModel(this.GetXmpMetadata("tiff:Model")); this.SetSize(this.GetActiveDocWidth()); this.SetColorTemp(this.GetXmpMetadata("crs:Temperature")); this.SetTint(this.GetXmpMetadata("crs:Tint")); this.SetShadows(this.GetXmpMetadata("crs:Shadows")); this.SetBrightness(this.GetXmpMetadata("crs:Brightness")); this.SetContrast(this.GetXmpMetadata("crs:Contrast")); this.SetSaturation(this.GetXmpMetadata("crs:Saturation")); this.SetExposure(this.GetXmpMetadata("crs:Exposure")); this.SetShadowTint(this.GetXmpMetadata("crs:ShadowTint")); this.SetRedHue(this.GetXmpMetadata("crs:RedHue")); this.SetRedSat(this.GetXmpMetadata("crs:RedSaturation")); this.SetGreenHue(this.GetXmpMetadata("crs:GreenHue")); this.SetGreenSat(this.GetXmpMetadata("crs:GreenSaturation")); this.SetBlueHue(this.GetXmpMetadata("crs:BlueHue")); this.SetBlueSat(this.GetXmpMetadata("crs:BlueSaturation")); this.version = this.GetXmpMetadata("crs:Version"); this.version = this.version.replace(/[^0123456789\.x]/g, ""); this.numVersion = parseFloat(this.version); // reads the number up to a non-interpretable character, i.e. 4.5.6 becomes 4.5 this.profileVersion = this.GetXmpMetadata("crs:CameraProfile"); // In ACR versions prior to 3.0 there was no support for custom tone curves if (this.numVersion >= 3.0-0.00001) this.curveSupported = true; if (this.numVersion >= 4.0-0.00001) this.parametricCurveSupported = true; if (this.numVersion >= 4.1-0.00001) this.acr41features = true; if (this.numVersion > 4.4) { if (!confirm("The installed version (" + this.version + ") of Adobe Camera Raw (ACR) has not been tested with ACR Calibrator L.\n" + "If you are running the latest version of ACR, check for updates to the ACR Calibrator script:\n" + " http://www.21stcenturyshoebox.com/tools/ACRcalibrator.html\n\n" + "Do you like to continue running the script anyway? Note that this might lead to errors.")) throw ("Script aborted by the user."); } if (this.acr41features == true) this.SetVibrance(this.GetXmpMetadata("crs:Vibrance")); } // ------------------------------------------------------- // Linearize() // Hard-coded linearization procedure // ------------------------------------------------------- Acr.prototype['Linearize'] = function () { this.SetBrightness(0); this.SetContrast(0); this.SetShadows(0); this.linearCurve = true; this.curveValues = Array(0,0,255,255); debug.shad = acr.shad; debug.brgh = acr.brgh; debug.cntr = acr.cntr; debug.crvText = "((0,0),(255,255))"; debug.crvValid = true; } // ------------------------------------------------------- // UsePreviousCurve() // ------------------------------------------------------- Acr.prototype['UsePreviousCurve'] = function () { debug.shad = acr.shad; debug.brgh = acr.brgh; debug.cntr = acr.cntr; } // ------------------------------------------------------- // ResetCalibration() // Hard-coded default procedure // ------------------------------------------------------- Acr.prototype['ResetCalibration'] = function () { this.SetExposure(0); this.SetColorTemp(5000); this.SetTint(0); this.SetSaturation(0); this.SetShadowTint(0); this.SetRedHue(0); this.SetRedSat(0); this.SetGreenHue(0); this.SetGreenSat(0); this.SetBlueHue(0); this.SetBlueSat(0); this.SetVibrance(0); } // ------------------------------------------------------- // GetXmpMetadata() // Returns the meta data labeled by tag // ------------------------------------------------------- Acr.prototype['GetXmpMetadata'] = function (tag) { var i, metadata, result; var reFind = new RegExp("<" + tag + ">.*"); var reStart = new RegExp("<" + tag + ">"); var reEnd = new RegExp(""); metadata = activeDocument.xmpMetadata.rawData.split("\n"); result = ""; for ( i=0; i1)) { var i; var curvdesc = new ActionList(); for (i=0 ; i Math.PI) Dh = h2p-h1p-2*Math.PI; else Dh = h2p-h1p + 2*Math.PI; DH = 2*Math.sqrt(C1p*C2p)*Math.sin(0.5*Dh); L = 0.5*(L1 + L2); C = 0.5*(C1p + C2p); if (C1p*C2p == 0) h = h1p+h2p; else if (Math.abs(h1p-h2p) <= Math.PI) h = 0.5*(h1p+h2p); else if (h1p+h2p < 2*Math.PI) h = 0.5*(h1p+h2p+2*Math.PI); else h = 0.5*(h1p+h2p - 2*Math.PI); T = 1-0.17* Math.cos(h - Math.PI/6) + 0.24*Math.cos(2*h) + 0.32*Math.cos(3*h + Math.PI/30) -0.2 * Math.cos(4*h-(63/180)*Math.PI); dtheta = (Math.PI/6)*Math.exp(-Math.pow((h-Math.PI*275/180)/(25/180),2)); Rc = 2*Math.sqrt(Math.pow(C,7)/(Math.pow(C,7)+Math.pow(25,7))); Sl = 1+ 0.015*Math.pow(L-50,2)/Math.sqrt(20+Math.pow(L-50,2)); Sc = 1+ 0.045*C; Sh = 1+ 0.015*C*T; Rt = - Math.sin(2*dtheta)*Rc; return Math.sqrt(Math.pow(DL/(DE_kL*Sl),2) + Math.pow(DC/(DE_kC*Sc),2) + Math.pow(DH/(DE_kH*Sh),2) + Rt*(DC/(DE_kC*Sc))*(DH/(DE_kH*Sh))); } // ------------------------------------------------------- // GetWbError() // Returns white balance error of patch 19 // ------------------------------------------------------- ColorChecker.prototype['GetWbError'] = function () { acr.Open(); var err = cc.DeltaC(19); acr.Close(); return err; } // ------------------------------------------------------- // GetGrayError() // Returns RMS luminance error of gray patches // ------------------------------------------------------- ColorChecker.prototype['GetGrayError'] = function () { acr.Open(); var err = 0; var i; var weightsum = 0; for (i=18 ; i<=23 ; i++) { err += weights[i]*Math.pow(cc.DeltaL(i),2); weightsum += weights[i]; } err /= weightsum; err = Math.sqrt(err); // RMS acr.Close(); return err; } // ------------------------------------------------------- // GetShadowTintError() // Returns white balance error of patch 23 // ------------------------------------------------------- ColorChecker.prototype['GetShadowTintError'] = function () { acr.Open(); var err = cc.DeltaC(23); acr.Close(); return err; } // ------------------------------------------------------- // GetColorError() // Returns error of all coloured patches // ------------------------------------------------------- ColorChecker.prototype['GetColorError'] = function () { acr.Open(); var i; var err = 0; var weightsum=0; for (i=0 ; i<18 ; i++) { err += weights[i]*cc.DeltaE2k(i); weightsum += weights[i]; } err /= weightsum; acr.Close(); return err; } // ------------------------------------------------------- // WhiteBalance() // Performs a white balance // ------------------------------------------------------- ColorChecker.prototype['WhiteBalance'] = function (n) { var settings; var optimizer; var msg = "White Balance"; if ( n == 2 ) msg = "White Balance (2nd pass)"; optimizer = new Simplex(2, msg); optimizer.SetDimension(0, 2000, 50000, 50, "SetColorTemp"); optimizer.SetDimension(1, -150, 150, 1, "SetTint"); optimizer.DefineOptimization(-1, "GetWbError", 0.1, undefined, undefined, 0); settings = new Array(2); settings[0] = acr.temp; settings[1] = acr.tint; optimizer.Optimize(settings); // initial settings debug.temp = acr.temp; debug.tint = acr.tint; debug.DisplayStatus(" "); //return optimizer.EvaluateResults(0.1); } // ------------------------------------------------------- // GrayAdjust() // Optimizes gray patches // ------------------------------------------------------- ColorChecker.prototype['GrayAdjust'] = function () { var settings; var optimizer; optimizer = new Simplex(4, "Gray Patches"); optimizer.SetDimension(0, -4, 4, .05, "SetExposure"); optimizer.SetDimension(1, 0, 100, 1, "SetShadows"); optimizer.SetDimension(2, 0, 150, 1, "SetBrightness"); optimizer.SetDimension(3, -50, 100, 1, "SetContrast"); optimizer.DefineOptimization(-1, "GetGrayError", 0, undefined, undefined, 0); settings = new Array(4); settings[0] = acr.exps; settings[1] = acr.shad; settings[2] = acr.brgh; settings[3] = acr.cntr; optimizer.Optimize(settings); // initial settings debug.exps = acr.exps; debug.shad = acr.shad; debug.brgh = acr.brgh; debug.cntr = acr.cntr; debug.DisplayStatus(" "); //return optimizer.EvaluateResults(0.6); } // ------------------------------------------------------- // ExpAdjust() // Optimizes gray patches by changing exposure ONLY // ------------------------------------------------------- ColorChecker.prototype['ExpAdjust'] = function () { var settings; var optimizer; optimizer = new Simplex(1, "Exposure"); optimizer.SetDimension(0, -4, 4, .05, "SetExposure"); optimizer.DefineOptimization(-1, "GetGrayError", 0, undefined, undefined, 0); settings = new Array(1); settings[0] = acr.exps; optimizer.Optimize(settings); // initial settings debug.exps = acr.exps; debug.DisplayStatus(" "); } // ------------------------------------------------------- // ShadowTintAdjust() // Optimizes shadow tint control for neutrality in // black patch // ------------------------------------------------------- ColorChecker.prototype['ShadowTintAdjust'] = function () { var settings; var optimizer; optimizer = new Simplex(1, "Shadow Tint"); optimizer.SetDimension(0, -100, 100, 1, "SetShadowTint"); optimizer.DefineOptimization(-1, "GetShadowTintError", 0, undefined, undefined, 0); settings = new Array(1); settings[0] = acr.blkb; optimizer.Optimize(settings); // initial settings debug.blkb = acr.blkb; debug.DisplayStatus(" "); //return optimizer.EvaluateResults(0); } // ------------------------------------------------------- // AllCalibration() // Adjusts all colour controls on the ACR calibration tab // simultanuously (except shadow tint). // ------------------------------------------------------- ColorChecker.prototype['AllCalibration'] = function (n) { var settings; var optimizer; var msg = "Colour Calibration"; if ( n == 2 ) msg = "Colour Calibration (2nd pass)"; if ( n == 3 ) msg = "Colour Calibration (3rd pass)"; if (n > 3) msg = "Colour Calibration (" + n + "th pass)"; optimizer = new Simplex(6, msg); optimizer.SetDimension(0, -100, 100, 1, "SetRedHue"); optimizer.SetDimension(1, -100, 100, 1, "SetRedSat"); optimizer.SetDimension(2, -100, 100, 1, "SetGreenHue"); optimizer.SetDimension(3, -100, 100, 1, "SetGreenSat"); optimizer.SetDimension(4, -100, 100, 1, "SetBlueHue"); optimizer.SetDimension(5, -100, 100, 1, "SetBlueSat"); optimizer.DefineOptimization(-1, "GetColorError", 0, undefined, undefined, numberOfPasses-1); settings = new Array(6); settings[0] = acr.rhue; settings[1] = acr.rsat; settings[2] = acr.ghue; settings[3] = acr.gsat; settings[4] = acr.bhue; settings[5] = acr.bsat; optimizer.Optimize(settings); // initial settings debug.rhue = acr.rhue; debug.rsat = acr.rsat; debug.ghue = acr.ghue; debug.gsat = acr.gsat; debug.bhue = acr.bhue; debug.bsat = acr.bsat; debug.DisplayStatus(" "); } // ======================================================= // Variable-size N-dimensional simplex optimization. // ======================================================= // ------------------------------------------------------- // Simplex() // Constructor // ------------------------------------------------------- function Simplex(nDim, name) { var i; this.name = name; this.nDim = nDim; // number of dimensions this.uv = new Array(); // array of vertices in user coordinates this.v = new Array(); // array of vertices in normalized coordinates this.r = new Array(); // array of results this.rCache = new Array(); // result cache this.rCacheSize = 0; // cache size this.rCacheHits = 0; // number of cache hits this.bestSettings = new Array(); // best settings found // Construct vertex arrays for ( i=0; i 0 ) { this.rCache[cacheIndex] = cacheValue; this.rCacheSize++; } } debug.files["cache"].close(); } } // ------------------------------------------------------- // SetDimension() // Sets user-space boundary and resolution requirements // ------------------------------------------------------- Simplex.prototype['SetDimension'] = function (d, uLo, uHi, resolution, fnSet) { this.uLo[d] = uLo; this.uHi[d] = uHi; this.uRes[d] = resolution; this.fnSet[d] = fnSet; } // ------------------------------------------------------- // NormalizeVertex() // Convert vertex from user space to normalized space // ------------------------------------------------------- Simplex.prototype['NormalizeVertex'] = function (i) { var d, m, b; for ( d=0; d this.r[1] ) { worst = 0; nextWorst = 1; } else { worst = 1; nextWorst = 0; } // Search... for ( i=0; i= this.r[best] ) { best = i; // new Best } } // Make the assignments if ( this.W < 0 ) { // we're ranking vertices of the first simplex, so we assign W // to the absolute worst point this.W = worst; this.N = nextWorst; } else { // otherwise W will have been assigned to the previous N in // Step() so we honor that here and just assign N (and B). if ( this.W != worst ) { this.N = worst; } else { this.N = nextWorst; } } // B is always best this.B = best; // Check for new best result and save if ( this.r[this.B] > this.bestResult ) { this.bestResult = this.r[this.B]; this.DenormalizeVertex(this.B); for ( d=0; d 1 ) { bOutOfBounds = 1; } } this.DenormalizeVertex(i); var bShowStatus = true; if ( bOutOfBounds ) { this.r[i] = Number.NEGATIVE_INFINITY; // return extremely bad result to force a contraction bShowStatus = false; } else { // check cache first. if not present, do the more time-consuming evaluation cacheIndex = ""; for ( d=0; d 0 ) { this.r[i] = result; // maximization: this is the default for RankVertices() } else { this.r[i] = -Math.abs(result); // zero: optimize for -abs(result) since RankVertices() assumes maximization } } var ch; ch = " "; switch (i) { case this.B: ch = "B "; break; case this.N: ch = "N "; break; case this.W: ch = "W "; break; case this.R: ch = "R "; break; case this.E: ch = "E "; break; case this.Cr: ch = "Cr "; break; case this.Cw: ch = "Cw "; break; } debug.WriteLog("dbg", " " + i + " " + ch + "[ "); for ( d=0; d 0.75) ? (-0.25) : (0.25); } p = (Math.sqrt(this.nDim+1) + this.nDim - 1) / (this.nDim * Math.sqrt(2)); q = (Math.sqrt(this.nDim+1) - 1) / (this.nDim * Math.sqrt(2)); // Build the tilted simplex for ( i=1; i this.r[this.B] ) { // We're moving in a desirable direction. Try expanding. // R > B calculate and evaluate E debug.WriteLog("dbg", " Expanding.\n"); this.EvaluateVertex(this.E); if ( this.r[this.E] >= this.r[this.B] ) { // Expansion worked. // E >= B use simplex B..NE and go to step 3 useVertex = this.E; debug.WriteLog("dbg", " Expansion worked. Keeping E.\n"); } else { // Expansion failed. // E < B use simplex B..NR and go to step 3 useVertex = this.R; debug.WriteLog("dbg", " Expansion failed. Keeping R.\n"); } } else { // We're moving in an undesireable direction. Try contracting. // R < N debug.WriteLog("dbg", " Contracting.\n"); if ( this.r[this.R] == Number.NEGATIVE_INFINITY ) { // Boundary Violation debug.WriteLog("dbg", " Boundary Violation.\n"); this.EvaluateVertex(this.Cr); // evaluate Cr if ( this.r[this.Cr] != Number.NEGATIVE_INFINITY ) { // in bounds! use B..NCr and go to step 3 useVertex = this.Cr; debug.WriteLog("dbg", " Keeping Cr.\n"); } else { // left with a Cw retraction. use B..NCw and go to step 3 this.EvaluateVertex(this.Cw); useVertex = this.Cw; debug.WriteLog("dbg", " Keeping Cw.\n"); } } else { // No boundary violation if ( this.r[this.R] >= this.r[this.W] ) { // R >= W calculate and evaluate Cr. use simplex B..NCr and go to step 3 this.EvaluateVertex(this.Cr); useVertex = this.Cr; debug.WriteLog("dbg", " Keeping Cr.\n"); } else { // R < W calculate and evaluate Cw. use simplex B..NCw and go to step 3 this.EvaluateVertex(this.Cw); useVertex = this.Cw; debug.WriteLog("dbg", " Keeping Cw.\n"); } } } debug.WriteLog("dbg", "-----------------------------------------------------------------\n"); // step 3 // discard W try { for ( d=0; d 0 ) { bGoalReached = (this.bestResult > this.goal) ? 1 : 0; } else { bGoalReached = (Math.abs(this.bestResult) < Math.abs(this.goal)) ? 1 : 0; } return bGoalReached; } // ------------------------------------------------------- // IsDegenerate() // Determines if simplex had degenerated to a single point // or is oscillating // ------------------------------------------------------- Simplex.prototype['IsDegenerate'] = function () { var i, d; var cacheIndex; var degenerate = 0; for ( i=0; i this.nDim*10 ) { degenerate = 1; } } return degenerate; } // ------------------------------------------------------- // Optimize() // Begins optimization process. Repeatedly calls Step() // to do the optimization until one of the exit criteria // defined by DefineOptimization() is reached. // startingPoint must be an array of nDim user-space // coordinates // ------------------------------------------------------- Simplex.prototype['Optimize'] = function (startingPoint) { var d; var reason; debug.DisplayStatus(""); this.startTime = new Date(); this.nSteps = 0; this.nRestarts = 0; this.bestResult = Number.NEGATIVE_INFINITY; // Evaluate starting point. for ( d=0; d= 0 ) { // simplex has degenerated to a point or is oscillating // restart using random points (best point is still retained) this.nRestarts++; if ( this.nRestarts <= this.restartLimit ) { for ( d=0; d 0 ) { // goal reached and simplex converged at least once reason = "Finished (Goal reached)."; break; } if ( this.GetElapsedMinutes() > this.timeLimit ) { // ran too long reason = "Terminated (Ran too long)."; break; } if ( this.nSteps > this.stepLimit ) { // too many steps reason = "Terminated (Too many steps)."; break; } if ( this.nRestarts > this.restartLimit ) { // too many restarts reason = "Terminated (Too many restarts)."; break; } this.Step(); debug.WriteLog("log", log); log = ""; } } log += "end = [ "; for ( d=0; d= 3 ) { throw ("Error writing to status window. Script aborted."); } } // ------------------------------------------------------- // WaitForRedraw() // ------------------------------------------------------- Debug.prototype['WaitForRedraw'] = function () { var eventWait = charIDToTypeID('Wait'); var enumRedrawComplete = charIDToTypeID('RdCm'); var typeState = charIDToTypeID('Stte'); var keyState = charIDToTypeID('Stte'); var desc = new ActionDescriptor(); desc.putEnumerated(keyState, typeState, enumRedrawComplete); executeAction(eventWait, desc, DialogModes.NO); } // ------------------------------------------------------- // CountLines() // ------------------------------------------------------- Debug.prototype['CountLines'] = function (data) { var lines = data.split("\r"); return lines.length - 1; } // ------------------------------------------------------- // NewStatusWindow() // ------------------------------------------------------- Debug.prototype['NewStatusWindow'] = function () { var statusWnd, bOldWndExists = true; try { statusWnd = documents.getByName("ACR Calibrator Status Window"); } catch (e) { bOldWndExists = false; } if ( bOldWndExists ) { statusWnd.close(SaveOptions.DONOTSAVECHANGES); } // Create new document var desc1 = new ActionDescriptor(); var desc2 = new ActionDescriptor(); desc2.putString( charIDToTypeID( "Nm " ), "ACR Calibrator Status Window" ); desc2.putClass( charIDToTypeID( "Md " ), charIDToTypeID( "RGBM" ) ); desc2.putUnitDouble( charIDToTypeID( "Wdth" ), charIDToTypeID( "#Rlt" ), 640.000000 ); desc2.putUnitDouble( charIDToTypeID( "Hght" ), charIDToTypeID( "#Rlt" ), 480.000000 ); desc2.putUnitDouble( charIDToTypeID( "Rslt" ), charIDToTypeID( "#Rsl" ), 72.000000 ); desc2.putDouble( stringIDToTypeID( "pixelScaleFactor" ), 1.000000 ); desc2.putEnumerated( charIDToTypeID( "Fl " ), charIDToTypeID( "Fl " ), charIDToTypeID( "Wht " ) ); desc2.putInteger( charIDToTypeID( "Dpth" ), 8 ); desc2.putString( stringIDToTypeID( "profile" ), "ProPhoto RGB" ); desc1.putObject( charIDToTypeID( "Nw " ), charIDToTypeID( "Dcmn" ), desc2 ); executeAction(charIDToTypeID("Mk "), desc1, DialogModes.NO); this.statusWnd = documents.getByName("ACR Calibrator Status Window"); // Select Status Window activeDocument = this.statusWnd; // Create the text layers var color = new SolidColor(); color.rgb = new RGBColor(); this.blackTextLayer = activeDocument.artLayers.add(); this.blackTextLayer.kind = LayerKind.TEXT; this.blackTextLayer.name = "blackText"; this.blackTextLayer.textItem.position = Array(activeDocument.width*0.005, activeDocument.height*0.03); this.blackTextLayer.textItem.size = 14; this.blackTextLayer.textItem.font = "Verdana"; color.rgb.red = 0; color.rgb.green = 0; color.rgb.blue = 0; this.blackTextLayer.textItem.color = color; this.redTextLayer = activeDocument.artLayers.add(); this.redTextLayer.kind = LayerKind.TEXT; this.redTextLayer.name = "redText"; this.redTextLayer.textItem.position = Array(activeDocument.width*0.005, activeDocument.height*0.03); this.redTextLayer.textItem.size = 14; this.redTextLayer.textItem.font = "Verdana"; color.rgb.red = 255; color.rgb.green = 0; color.rgb.blue = 0; this.redTextLayer.textItem.color = color; this.grayTextLayer = activeDocument.artLayers.add(); this.grayTextLayer.kind = LayerKind.TEXT; this.grayTextLayer.name = "grayText"; this.grayTextLayer.textItem.position = Array(activeDocument.width*0.005, activeDocument.height*0.03); this.grayTextLayer.textItem.size = 14; this.grayTextLayer.textItem.font = "Verdana"; color.rgb.red = 128; color.rgb.green = 128; color.rgb.blue = 128; this.grayTextLayer.textItem.color = color; this.resultsTextLayer = activeDocument.artLayers.add(); this.resultsTextLayer.kind = LayerKind.TEXT; this.resultsTextLayer.name = "resultsText"; this.resultsTextLayer.textItem.position = Array(activeDocument.width*0.605, activeDocument.height*0.03); this.resultsTextLayer.textItem.size = 14; this.resultsTextLayer.textItem.font = "Verdana"; color.rgb.red = 0; color.rgb.green = 0; color.rgb.blue = 0; this.resultsTextLayer.textItem.color = color; this.SelectRawWnd(); } // ------------------------------------------------------- // DisplayStatus() // ------------------------------------------------------- Debug.prototype['DisplayStatus'] = function (data) { var lines = data.split("\r"); if ( data.length > 0 ) { this.redText = lines[0] + "\r"; this.refreshRedText = true; } this.grayText = ""; for ( i=1; i 0 && lines[0] != "Done.") { this.grayText += " Elapsed Time: " + this.GetElapsedMinutes() + " mins. AROPM: " + Math.round(acr.nOpens/this.GetElapsedMinutes()*100)/100; } this.refreshGrayText = true; this.Refresh(); } // ------------------------------------------------------- // AddStatusLine() // ------------------------------------------------------- Debug.prototype['AddStatusLine'] = function (status) { this.blackText += status + "\r"; this.refreshBlackText = true; } // ======================================================= // Options Dialog Menu Routines // ======================================================= // ------------------------------------------------------- // Options() // Constructor: menu defaults from global defaults // ------------------------------------------------------- function Options() { this.resetSliders = resetSliders; this.illumType = illumType; this.calMethod = calMethod; this.calShadowTint = calShadowTint; this.ignoreBrightness = ignoreBrightness; this.DE_kL = DE_kL; this.DE_kH = DE_kH; this.DE_kC = DE_kC; this.numberOfPasses = numberOfPasses; this.weights = new Array(24); for (var i=0 ; i<24 ; i++) this.weights[i] = weights[i]; this.checkOptions = false; // ok or cancel ? this.dlg; // the dialog menu window return; } // ------------------------------------------------------- // Options.rePop() // repopulate global variables // ------------------------------------------------------- Options.prototype['rePop'] = function() { resetSliders = this.resetSliders; illumType = this.illumType; calMethod = this.calMethod; calShadowTint = this.calShadowTint; ignoreBrightness = this.ignoreBrightness; DE_kL = this.DE_kL; DE_kH = this.DE_kH; DE_kC = this.DE_kC; numberOfPasses =this.numberOfPasses; for (var i=0 ; i<24 ; i++) weights[i] = this.weights[i]; return; } // ------------------------------------------------------- // Options.buildMenu() // create the menu, panels, and option boxes // ------------------------------------------------------- Options.prototype['buildMenu'] = function() { var listValues; //---------------------------------------- // create the dialog menu window (x,y,w,h) //---------------------------------------- this.dlg = new Window('dialog', "Calibration Options", undefined); this.dlg.bounds = [100, 100, 700, 700]; // dialog window position & size (l,t,r,b) //----------------------------------- // create the options panel (l,t,r,b) //----------------------------------- this.dlg.optPanel = this.dlg.add('panel', [10,10,590,595], 'Options'); // options panel //------------------------------ // ok & cancel buttons (l,t,r,b) //------------------------------ this.dlg.optPanel.canBtn = this.dlg.optPanel.add('button', [150,545,220,565], 'Cancel', {name:'cancel'}); this.dlg.optPanel.canBtn.helpTip = "Cancel script execution"; this.dlg.optPanel.okBtn = this.dlg.optPanel.add('button', [40,545,110,565], 'OK', {name:'ok'}); this.dlg.optPanel.okBtn.helpTip = "Validate and Accept changes"; this.dlg.optPanel.okBtn.onClick = function () // tell onClose event to Check Options {opt.checkOptions = true; this.parent.parent.close(1);}; //----------------------------- // option check boxes (l,t,r,b) //----------------------------- this.dlg.optPanel.resetCb = this.dlg.optPanel.add('checkbox', [40,20,250,40], 'Reset all calibration sliders on start'); this.dlg.optPanel.resetCb.helpTip = "Re-initialize all settings before calibration"; this.dlg.optPanel.resetCb.value = this.resetSliders; //------------------------------------------- // Target Illuminant drop down list (l,t,r,b) //------------------------------------------- illumListValues = new Array("D50", "D65", "A"); this.dlg.optPanel.illumTxtBx = this.dlg.optPanel.add('statictext', [40,50,150,70], 'Target Illuminant:'); this.dlg.optPanel.illumLstBx = this.dlg.optPanel.add('dropdownlist', [165,50,220,70], illumListValues); this.dlg.optPanel.illumLstBx.helpTip = "Target Illuminant values based on CIE CCT: D50, D65, or A"; switch(this.illumType) { // CIE Illuminant drop down list box case "D50": this.dlg.optPanel.illumLstBx.items[0].selected = true; break; case "D65": this.dlg.optPanel.illumLstBx.items[1].selected = true; break; case "A": this.dlg.optPanel.illumLstBx.items[2].selected = true; break; default: alert("buildMenu() Target Illuminant must be D50, D65, or C"); break; } //------------------------------------------- // Calibration Method drop down list (l,t,r,b) //------------------------------------------- methodListValues = new Array("scene-referred", "use XMP data", "fit curve"); this.dlg.optPanel.methodTxtBx = this.dlg.optPanel.add('statictext', [40,80,150,100], 'Calibration Method:'); this.dlg.optPanel.methodLstBx = this.dlg.optPanel.add('dropdownlist', [165,80,300,100], methodListValues); this.dlg.optPanel.methodLstBx.helpTip = "Method of tone curve calibration"; switch(this.calMethod) { // Calibration method drop down list box case "linearPreset": this.dlg.optPanel.methodLstBx.items[0].selected = true; break; case "useXMP": this.dlg.optPanel.methodLstBx.items[1].selected = true; break; case "curveFit": this.dlg.optPanel.methodLstBx.items[2].selected = true; break; default: alert("buildMenu() Calibration Method must be either linearPreset, useXMP, or curveFit"); break; } this.dlg.optPanel.calShadowTintCb = this.dlg.optPanel.add('checkbox', [40,110,200,130], 'Calibrate Shadow Tint'); this.dlg.optPanel.calShadowTintCb.helpTip = "Calibrate Shadow Tint slider as well (not recommended)"; this.dlg.optPanel.calShadowTintCb.value = this.calShadowTint; //-------------------------- // error calculation options //-------------------------- this.dlg.optPanel.errorPanel = this.dlg.optPanel.add('panel', [40,140,230,280], 'Error calculation method'); this.dlg.optPanel.errorPanel.ignoreBrightnessCb = this.dlg.optPanel.errorPanel.add('checkbox', [5,5,200,35], 'Ignore brightness differences'); this.dlg.optPanel.errorPanel.ignoreBrightnessCb.helpTip = "Ignore brightness differences in DeltaE2000 calculation (recommended for methods other than linearPreset)"; this.dlg.optPanel.errorPanel.ignoreBrightnessCb.value = this.ignoreBrightness; this.dlg.optPanel.errorPanel.DE_kL = this.dlg.optPanel.errorPanel.add('statictext', [5,45,25,65], 'kL'); this.dlg.optPanel.errorPanel.DE_kL = this.dlg.optPanel.errorPanel.add('edittext', [30,45,70,65], this.DE_kL); this.dlg.optPanel.errorPanel.DE_kL.helpTip = "Inverse luminance weight for DeltaE 2000 calculation"; this.dlg.optPanel.errorPanel.DE_kH = this.dlg.optPanel.errorPanel.add('statictext',[5,75,25,95], 'kH'); this.dlg.optPanel.errorPanel.DE_kH = this.dlg.optPanel.errorPanel.add('edittext',[30,75,70,95], this.DE_kH); this.dlg.optPanel.errorPanel.DE_kH.helpTip = "Inverse hue weight for DeltaE 2000 calculation"; this.dlg.optPanel.errorPanel.DE_kC = this.dlg.optPanel.errorPanel.add('statictext',[5,105,25,125], 'kC'); this.dlg.optPanel.errorPanel.DE_kC = this.dlg.optPanel.errorPanel.add('edittext',[30,105,70,125], this.DE_kC); this.dlg.optPanel.errorPanel.DE_kC.helpTip = "Inverse chroma weight for DeltaE 2000 calculation"; //------------------------------ // Patch weight values (l,t,r,b) //------------------------------ this.dlg.optPanel.weightPanel = this.dlg.optPanel.add('panel', [40,295,520,495], 'Patch weights'); this.dlg.optPanel.weightPanel.p0 = this.dlg.optPanel.weightPanel.add('statictext', [5,10,80,30], 'Dark skin'); this.dlg.optPanel.weightPanel.p0 = this.dlg.optPanel.weightPanel.add('edittext', [80,10,110,30], this.weights[0]); this.dlg.optPanel.weightPanel.p1 = this.dlg.optPanel.weightPanel.add('statictext', [5,40,80,60], 'Light skin'); this.dlg.optPanel.weightPanel.p1 = this.dlg.optPanel.weightPanel.add('edittext', [80,40,110,60], this.weights[1]); this.dlg.optPanel.weightPanel.p2 = this.dlg.optPanel.weightPanel.add('statictext', [5,70,80,90], 'Blue sky'); this.dlg.optPanel.weightPanel.p2 = this.dlg.optPanel.weightPanel.add('edittext', [80,70,110,90], this.weights[2]); this.dlg.optPanel.weightPanel.p3 = this.dlg.optPanel.weightPanel.add('statictext', [5,100,80,120], 'Foliage'); this.dlg.optPanel.weightPanel.p3 = this.dlg.optPanel.weightPanel.add('edittext', [80,100,110,120], this.weights[3]); this.dlg.optPanel.weightPanel.p4 = this.dlg.optPanel.weightPanel.add('statictext', [5,130,80,150], 'Blue flower'); this.dlg.optPanel.weightPanel.p4 = this.dlg.optPanel.weightPanel.add('edittext', [80,130,110,150], this.weights[4]); this.dlg.optPanel.weightPanel.p5 = this.dlg.optPanel.weightPanel.add('statictext', [5,160,80,180], 'Bluish green'); this.dlg.optPanel.weightPanel.p5 = this.dlg.optPanel.weightPanel.add('edittext', [80,160,110,180], this.weights[5]); this.dlg.optPanel.weightPanel.p6 = this.dlg.optPanel.weightPanel.add('statictext', [125,10,200,30], 'Orange'); this.dlg.optPanel.weightPanel.p6 = this.dlg.optPanel.weightPanel.add('edittext', [200,10,230,30], this.weights[6]); this.dlg.optPanel.weightPanel.p7 = this.dlg.optPanel.weightPanel.add('statictext', [125,40,200,60], 'Purplish blue'); this.dlg.optPanel.weightPanel.p7 = this.dlg.optPanel.weightPanel.add('edittext', [200,40,230,60], this.weights[7]); this.dlg.optPanel.weightPanel.p8 = this.dlg.optPanel.weightPanel.add('statictext', [125,70,200,90], 'Moderate red'); this.dlg.optPanel.weightPanel.p8 = this.dlg.optPanel.weightPanel.add('edittext', [200,70,230,90], this.weights[8]); this.dlg.optPanel.weightPanel.p9 = this.dlg.optPanel.weightPanel.add('statictext', [125,100,200,120], 'Purple'); this.dlg.optPanel.weightPanel.p9 = this.dlg.optPanel.weightPanel.add('edittext', [200,100,230,120], this.weights[9]); this.dlg.optPanel.weightPanel.p10 = this.dlg.optPanel.weightPanel.add('statictext', [125,130,200,150], 'Yellow green'); this.dlg.optPanel.weightPanel.p10 = this.dlg.optPanel.weightPanel.add('edittext', [200,130,230,150], this.weights[10]); this.dlg.optPanel.weightPanel.p11 = this.dlg.optPanel.weightPanel.add('statictext', [125,160,200,180], 'Orange yellow'); this.dlg.optPanel.weightPanel.p11 = this.dlg.optPanel.weightPanel.add('edittext', [200,160,230,180], this.weights[11]); this.dlg.optPanel.weightPanel.p12 = this.dlg.optPanel.weightPanel.add('statictext', [245,10,320,30], 'Blue'); this.dlg.optPanel.weightPanel.p12 = this.dlg.optPanel.weightPanel.add('edittext', [320,10,350,30], this.weights[12]); this.dlg.optPanel.weightPanel.p13 = this.dlg.optPanel.weightPanel.add('statictext', [245,40,320,60], 'Green'); this.dlg.optPanel.weightPanel.p13 = this.dlg.optPanel.weightPanel.add('edittext', [320,40,350,60], this.weights[13]); this.dlg.optPanel.weightPanel.p14 = this.dlg.optPanel.weightPanel.add('statictext', [245,70,320,90], 'Red'); this.dlg.optPanel.weightPanel.p14 = this.dlg.optPanel.weightPanel.add('edittext', [320,70,350,90], this.weights[14]); this.dlg.optPanel.weightPanel.p15 = this.dlg.optPanel.weightPanel.add('statictext', [245,100,320,120], 'Yellow'); this.dlg.optPanel.weightPanel.p15 = this.dlg.optPanel.weightPanel.add('edittext', [320,100,350,120], this.weights[15]); this.dlg.optPanel.weightPanel.p16 = this.dlg.optPanel.weightPanel.add('statictext', [245,130,320,150], 'Magenta'); this.dlg.optPanel.weightPanel.p16 = this.dlg.optPanel.weightPanel.add('edittext', [320,130,350,150], this.weights[16]); this.dlg.optPanel.weightPanel.p17 = this.dlg.optPanel.weightPanel.add('statictext', [245,160,320,180], 'Cyan'); this.dlg.optPanel.weightPanel.p17 = this.dlg.optPanel.weightPanel.add('edittext', [320,160,350,180], this.weights[17]); this.dlg.optPanel.weightPanel.p18 = this.dlg.optPanel.weightPanel.add('statictext', [365,10,440,30], 'White'); this.dlg.optPanel.weightPanel.p18 = this.dlg.optPanel.weightPanel.add('edittext', [440,10,470,30], this.weights[18]); this.dlg.optPanel.weightPanel.p19 = this.dlg.optPanel.weightPanel.add('statictext', [365,40,440,60], 'Neutral 8'); this.dlg.optPanel.weightPanel.p19 = this.dlg.optPanel.weightPanel.add('edittext', [440,40,470,60], this.weights[19]); this.dlg.optPanel.weightPanel.p20 = this.dlg.optPanel.weightPanel.add('statictext', [365,70,440,90], 'Neutral 6.5'); this.dlg.optPanel.weightPanel.p20 = this.dlg.optPanel.weightPanel.add('edittext', [440,70,470,90], this.weights[20]); this.dlg.optPanel.weightPanel.p21 = this.dlg.optPanel.weightPanel.add('statictext', [365,100,440,120], 'Neutral 5'); this.dlg.optPanel.weightPanel.p21 = this.dlg.optPanel.weightPanel.add('edittext', [440,100,470,120], this.weights[21]); this.dlg.optPanel.weightPanel.p22 = this.dlg.optPanel.weightPanel.add('statictext', [365,130,440,150], 'Neutral 3.5'); this.dlg.optPanel.weightPanel.p22 = this.dlg.optPanel.weightPanel.add('edittext', [440,130,470,150], this.weights[22]); this.dlg.optPanel.weightPanel.p23 = this.dlg.optPanel.weightPanel.add('statictext', [365,160,440,180], 'Black'); this.dlg.optPanel.weightPanel.p23 = this.dlg.optPanel.weightPanel.add('edittext', [440,160,470,180], this.weights[23]); //----------------------- // number of passes //---------------------- numberOfPassesValues = new Array("1", "2","3","4","5"); this.dlg.optPanel.numberOfPassesTxtBx = this.dlg.optPanel.add('statictext', [40,510,140,530], 'Calibration passes'); this.dlg.optPanel.numberOfPassesLstBx = this.dlg.optPanel.add('dropdownlist', [150,510,190,530], numberOfPassesValues); this.dlg.optPanel.numberOfPassesLstBx.helpTip = "Number of optimization passes"; if ((this.numberOfPasses >= 1) && (this.numberOfPasses <= 5)) this.dlg.optPanel.numberOfPassesLstBx.items[this.numberOfPasses-1].selected = true; else alert("buildMenu() number of passes must be in the range 1-5"); return; } // ------------------------------------------------------- // Options.showMenu() // ------------------------------------------------------- Options.prototype['showMenu'] = function() { var retCd; var testNum; // --------------------------------------------------------- // dlg.onClose() event handler: validate the current options // --------------------------------------------------------- this.dlg.onClose = function () { var rc = true; var itemSelected = 0; // alert("onClose opt.checkOptions=" + opt.checkOptions); if (opt.checkOptions) { // OK button opt.checkOptions = false; // reset it opt.resetSliders = this.optPanel.resetCb.value; itemSelected = Math.round(this.optPanel.illumLstBx.selection); // target illuminent switch(itemSelected) { case 0: opt.illumType = "D50"; break; case 1: opt.illumType = "D65"; break; case 2: opt.illumType = "A"; break; default: alert("showMenu() invalid target illuminant [" + itemSelected + "]"); rc = false; break; } itemSelected = Math.round(this.optPanel.methodLstBx.selection); // calibration method switch(itemSelected) { case 0: opt.calMethod = "linearPreset"; break; case 1: opt.calMethod = "useXMP"; break; case 2: opt.calMethod = "curveFit"; break; default: alert("showMenu() invalid calibration method [" + itemSelected + "]"); rc = false; break; } opt.calShadowTint = this.optPanel.calShadowTintCb.value; opt.ignoreBrightness = this.optPanel.errorPanel.ignoreBrightnessCb.value; itemSelected = Math.round(this.optPanel.numberOfPassesLstBx.selection); // number of passes opt.numberOfPasses = itemSelected + 1; testNum = new Number(this.optPanel.errorPanel.DE_kL.text); if (isNaN(testNum)) { rc = false; this.optPanel.errorPanel.DE_kL.text = opt.DE_kL; } else { opt.DE_kL = testNum; } testNum = new Number(this.optPanel.errorPanel.DE_kH.text); if (isNaN(testNum)) { rc = false; this.optPanel.errorPanel.DE_kH.text = opt.DE_kH; } else { opt.DE_kH = testNum; } testNum = new Number(this.optPanel.errorPanel.DE_kC.text); if (isNaN(testNum)) { rc = false; this.optPanel.errorPanel.DE_kC.text = opt.DE_kC; } else { opt.DE_kC = testNum; } testNum = new Number(this.optPanel.weightPanel.p0.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p0.text = opt.weights[0]; } else { opt.weights[0] = testNum; } testNum = new Number(this.optPanel.weightPanel.p1.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p1.text = opt.weights[1]; } else { opt.weights[1] = testNum; } testNum = new Number(this.optPanel.weightPanel.p2.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p2.text = opt.weights[2]; } else { opt.weights[2] = testNum; } testNum = new Number(this.optPanel.weightPanel.p3.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p3.text = opt.weights[3]; } else { opt.weights[3] = testNum; } testNum = new Number(this.optPanel.weightPanel.p4.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p4.text = opt.weights[4]; } else { opt.weights[4] = testNum; } testNum = new Number(this.optPanel.weightPanel.p5.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p5.text = opt.weights[5]; } else { opt.weights[5] = testNum; } testNum = new Number(this.optPanel.weightPanel.p6.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p6.text = opt.weights[6]; } else { opt.weights[6] = testNum; } testNum = new Number(this.optPanel.weightPanel.p7.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p7.text = opt.weights[7]; } else { opt.weights[7] = testNum; } testNum = new Number(this.optPanel.weightPanel.p8.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p8.text = opt.weights[8]; } else { opt.weights[8] = testNum; } testNum = new Number(this.optPanel.weightPanel.p9.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p9.text = opt.weights[9]; } else { opt.weights[9] = testNum; } testNum = new Number(this.optPanel.weightPanel.p10.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p10.text = opt.weights[10]; } else { opt.weights[10] = testNum; } testNum = new Number(this.optPanel.weightPanel.p11.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p11.text = opt.weights[11]; } else { opt.weights[11] = testNum; } testNum = new Number(this.optPanel.weightPanel.p12.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p12.text = opt.weights[12]; } else { opt.weights[12] = testNum; } testNum = new Number(this.optPanel.weightPanel.p13.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p13.text = opt.weights[13]; } else { opt.weights[13] = testNum; } testNum = new Number(this.optPanel.weightPanel.p14.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p14.text = opt.weights[14]; } else { opt.weights[14] = testNum; } testNum = new Number(this.optPanel.weightPanel.p15.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p15.text = opt.weights[15]; } else { opt.weights[15] = testNum; } testNum = new Number(this.optPanel.weightPanel.p16.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p16.text = opt.weights[16]; } else { opt.weights[16] = testNum; } testNum = new Number(this.optPanel.weightPanel.p17.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p17.text = opt.weights[17]; } else { opt.weights[17] = testNum; } testNum = new Number(this.optPanel.weightPanel.p18.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p18.text = opt.weights[18]; } else { opt.weights[18] = testNum; } testNum = new Number(this.optPanel.weightPanel.p19.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p19.text = opt.weights[19]; } else { opt.weights[19] = testNum; } testNum = new Number(this.optPanel.weightPanel.p20.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p20.text = opt.weights[20]; } else { opt.weights[20] = testNum; } testNum = new Number(this.optPanel.weightPanel.p21.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p21.text = opt.weights[21]; } else { opt.weights[21] = testNum; } testNum = new Number(this.optPanel.weightPanel.p22.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p22.text = opt.weights[22]; } else { opt.weights[22] = testNum; } testNum = new Number(this.optPanel.weightPanel.p23.text); if (isNaN(testNum)) { rc = false; this.optPanel.weightPanel.p23.text = opt.weights[23]; } else { opt.weights[23] = testNum; } } // CANCEL button return(rc); } // end function: this.dlg.onClose() // -------------------- // show the dialog menu // -------------------- retCd = this.dlg.show(); return(retCd); } // ======================================================= // Color Utility Functions // ======================================================= // ------------------------------------------------------- // ProPhotoRGBColor() // Constructor // ------------------------------------------------------- function ProPhotoRGBColor() { // ProPhotoRGB specification from icc file this.vR = new Vector1x3(0x0CC34/0x10000, 0x049BD/0x10000, 0x00000/0x10000); // red this.vG = new Vector1x3(0x0229C/0x10000, 0x0B63E/0x10000, 0x00000/0x10000); // green this.vB = new Vector1x3(0x00806/0x10000, 0x00006/0x10000, 0x0D32D/0x10000); // blue this.vW = new Vector1x3(0x0F6D6/0x10000, 0x10000/0x10000, 0x0D32C/0x10000); // white this.gamma = 461/256; // Compute RGB/XYZ Conversion Matrix var vS = (new Matrix3x3(this.vR, this.vG, this.vB)).Inverse().Multiply(this.vW); this.M = new Matrix3x3(this.vR.Multiply(vS.a11), this.vG.Multiply(vS.a12), this.vB.Multiply(vS.a13)); } // ------------------------------------------------------- // SetRGB() // ------------------------------------------------------- ProPhotoRGBColor.prototype['SetRGB'] = function (r, g, b) { this.R = r; this.G = g; this.B = b; this.ComputeXYZ(); this.Computexy(); this.ComputeLab(); this.ComputeLuv(); this.ComputeCHS(); } // ------------------------------------------------------- // ComputeXYZ() // ------------------------------------------------------- ProPhotoRGBColor.prototype['ComputeXYZ'] = function () { // Convert to linear, normalized rgb var R = Math.pow( (this.R/255), this.gamma); var G = Math.pow( (this.G/255), this.gamma); var B = Math.pow( (this.B/255), this.gamma); var RGB = new Vector1x3(R, G, B); // Convert to CIE XYZ var XYZ = this.M.Multiply(RGB); this.X = XYZ.a11; this.Y = XYZ.a12; this.Z = XYZ.a13; } // ------------------------------------------------------- // Computexy() // ------------------------------------------------------- ProPhotoRGBColor.prototype['Computexy'] = function () { // Compute x,y chromaticities this.x = this.X / (this.X+this.Y+this.Z); this.y = this.Y / (this.X+this.Y+this.Z); } // ------------------------------------------------------- // ComputeLab() // ------------------------------------------------------- ProPhotoRGBColor.prototype['ComputeLab'] = function () { // Compute CIE L*a*b* var E = 216/24389; var K = 24389/27; var x2 = this.X/this.vW.a11; var y2 = this.Y/this.vW.a12; var z2 = this.Z/this.vW.a13; if ( x2 > E ) Fx = Math.pow(x2, 1/3); else Fx = (K*x2 + 16) / 116; if ( y2 > E ) Fy = Math.pow(y2, 1/3); else Fy = (K*y2 + 16) / 116; if ( z2 > E ) Fz = Math.pow(z2, 1/3); else Fz = (K*z2 + 16) / 116; if ( y2 > E ) this.L = 116*Fy-16; else this.L = K*y2; this.a = 500*(Fx-Fy); this.b = 200*(Fy-Fz); } // ------------------------------------------------------- // ComputeLuv() // ------------------------------------------------------- ProPhotoRGBColor.prototype['ComputeLuv'] = function () { // Compute u' and v' this.up = 4*this.X / (this.X + 15*this.Y + 3*this.Z); this.vp = 9*this.Y / (this.X + 15*this.Y + 3*this.Z); // Compute ur' and vr' (u' and v' of reference white) var urp = 4*this.vW.a11 / (this.vW.a11 + 15*this.vW.a12 + 3*this.vW.a13); var vrp = 9*this.vW.a12 / (this.vW.a11 + 15*this.vW.a12 + 3*this.vW.a13); // Compute CIE L*u*v* this.u = 13 * this.L * (this.up - urp); // u* this.v = 13 * this.L * (this.vp - vrp); // v* } // ------------------------------------------------------- // ComputeCHS() // ------------------------------------------------------- ProPhotoRGBColor.prototype['ComputeCHS'] = function () { this.C = Math.sqrt( (this.a*this.a + this.b*this.b), 2 ); // chroma this.H = Math.atan2(this.a, this.b) * 180 / Math.PI; // hue this.S = this.C / this.L; // saturation } // ------------------------------------------------------- // MatchLuminosity() // ------------------------------------------------------- ProPhotoRGBColor.prototype['MatchLuminosity'] = function (Y) { var m = Math.pow(Y/this.Y,1/this.gamma); var scaleColor = new ProPhotoRGBColor(); this.SetRGB(m*this.R, m*this.G, m*this.B); } // ======================================================= // 3x3 Matrix Routines // ======================================================= // ------------------------------------------------------- // Matrix3x3() // Constructor // ------------------------------------------------------- function Matrix3x3(a11, a12, a13, a21, a22, a23, a31, a32, a33) { if ( (a11 instanceof Vector1x3) && (a12 instanceof Vector1x3) && (a13 instanceof Vector1x3) ) { // Three vectors have been specified. Constuct matrix with each vector as a row this.a11 = a11.a11; this.a12 = a11.a12; this.a13 = a11.a13; this.a21 = a12.a11; this.a22 = a12.a12; this.a23 = a12.a13; this.a31 = a13.a11; this.a32 = a13.a12; this.a33 = a13.a13; } else { // assume individual matrix elements have been specified and contruct it accordingly this.a11 = a11; this.a12 = a12; this.a13 = a13; this.a21 = a21; this.a22 = a22; this.a23 = a23; this.a31 = a31; this.a32 = a32; this.a33 = a33; } } // ------------------------------------------------------- // SetBradford() // Sets matrix to Bradford Chromatic Adapatation Matrix // ------------------------------------------------------- Matrix3x3.prototype['SetBradford'] = function () { this.a11 = 0.8951; this.a12 = -0.7502; this.a13 = 0.0389; this.a21 = 0.2664; this.a22 = 1.7135; this.a23 = -0.0685; this.a31 = -0.1614; this.a32 = 0.0368; this.a33 = 1.0296; } // ------------------------------------------------------- // Inverse() // Returns inverse of matrix. Does not alter this matrix. // ------------------------------------------------------- Matrix3x3.prototype['Inverse'] = function () { var det = this.Determinant(); if ( det == 0 ) throw("Attempted inverse of singular matrix."); var inv = new Matrix3x3( this.det2x2(this.a22,this.a23,this.a32,this.a33)/det, this.det2x2(this.a13,this.a12,this.a33,this.a32)/det, this.det2x2(this.a12,this.a13,this.a22,this.a23)/det, this.det2x2(this.a23,this.a21,this.a33,this.a31)/det, this.det2x2(this.a11,this.a13,this.a31,this.a33)/det, this.det2x2(this.a13,this.a11,this.a23,this.a21)/det, this.det2x2(this.a21,this.a22,this.a31,this.a32)/det, this.det2x2(this.a12,this.a11,this.a32,this.a31)/det, this.det2x2(this.a11,this.a12,this.a21,this.a22)/det ); return inv; } // ------------------------------------------------------- // Determinant() // Returns determinant of matrix. // ------------------------------------------------------- Matrix3x3.prototype['Determinant'] = function () { var det = this.a11*this.a22*this.a33; det -= this.a11*this.a23*this.a32; det -= this.a12*this.a21*this.a33; det += this.a12*this.a23*this.a31; det += this.a13*this.a21*this.a32; det -= this.a13*this.a22*this.a31; return det; } // ------------------------------------------------------- // det2x2() // Utility function to compute determinant of 2x2 matrix // ------------------------------------------------------- Matrix3x3.prototype['det2x2'] = function (a11, a12, a21, a22) { return ( (a11 * a22) - (a12 * a21) ); } // ------------------------------------------------------- // Multiply() // Multiplies lhs (left hand side) and matrix together. // lhs can be another 3x3 matrix, a 1x3 vector, or a scalar. // Returns appropriate result. This matrix is not changed. // ------------------------------------------------------- Matrix3x3.prototype['Multiply'] = function (lhs) { if ( lhs instanceof Matrix3x3 ) { // Matrix * Matrix return new Matrix3x3( lhs.a11*this.a11 + lhs.a12*this.a21 + lhs.a13*this.a31, lhs.a11*this.a12 + lhs.a12*this.a22 + lhs.a13*this.a32, lhs.a11*this.a13 + lhs.a12*this.a23 + lhs.a13*this.a33, lhs.a21*this.a11 + lhs.a22*this.a21 + lhs.a23*this.a31, lhs.a21*this.a12 + lhs.a22*this.a22 + lhs.a23*this.a32, lhs.a21*this.a13 + lhs.a22*this.a23 + lhs.a23*this.a33, lhs.a31*this.a11 + lhs.a32*this.a21 + lhs.a33*this.a31, lhs.a31*this.a12 + lhs.a32*this.a22 + lhs.a33*this.a32, lhs.a31*this.a13 + lhs.a32*this.a23 + lhs.a33*this.a33 ); } else if ( lhs instanceof Vector1x3 ) { // Vector * Matrix return new Vector1x3( lhs.a11*this.a11 + lhs.a12*this.a21 + lhs.a13*this.a31, lhs.a11*this.a12 + lhs.a12*this.a22 + lhs.a13*this.a32, lhs.a11*this.a13 + lhs.a12*this.a23 + lhs.a13*this.a33 ); } else { // Scalar * Matrix return new Matrix3x3( lhs*this.a11, lhs*this.a12, lhs*this.a13, lhs*this.a21, lhs*this.a22, lhs*this.a23, lhs*this.a31, lhs*this.a32, lhs*this.a33 ); } } // ------------------------------------------------------- // Display() // Uses an alert box to display matrix for debugging purposes // ------------------------------------------------------- Matrix3x3.prototype['Display'] = function () { var msg = ""; msg += Math.round(this.a11*1000000)/1000000 + "\t"; msg += Math.round(this.a12*1000000)/1000000 + "\t"; msg += Math.round(this.a13*1000000)/1000000 + "\n"; msg += Math.round(this.a21*1000000)/1000000 + "\t"; msg += Math.round(this.a22*1000000)/1000000 + "\t"; msg += Math.round(this.a23*1000000)/1000000 + "\n"; msg += Math.round(this.a31*1000000)/1000000 + "\t"; msg += Math.round(this.a32*1000000)/1000000 + "\t"; msg += Math.round(this.a33*1000000)/1000000 + "\n"; alert(msg); } // ======================================================= // 1x3 Matrix Routines // ======================================================= // ------------------------------------------------------- // Vector1x3() // Constructor // ------------------------------------------------------- function Vector1x3(a11, a12, a13) { this.a11 = a11; this.a12 = a12; this.a13 = a13; } // ------------------------------------------------------- // Multiply() // Multiplies lhs (left hand side) and vector together. // lhs must be a scalar. // Returns appropriate result. This vector is not changed. // ------------------------------------------------------- Vector1x3.prototype['Multiply'] = function (lhs) { // Scalar * Vector return new Vector1x3( lhs*this.a11, lhs*this.a12, lhs*this.a13 ); } // ------------------------------------------------------- // Display() // Uses an alert box to display vector for debugging purposes // ------------------------------------------------------- Vector1x3.prototype['Display'] = function () { var msg = ""; msg += Math.round(this.a11*1000000)/1000000 + "\t"; msg += Math.round(this.a12*1000000)/1000000 + "\t"; msg += Math.round(this.a13*1000000)/1000000 + "\n"; alert(msg); } // ======================================================= // Main Code // ======================================================= // Save user's preferences var rulerUnits = preferences.rulerUnits; var nonLinearHistory = preferences.nonLinearHistory; var numberOfHistoryStates = preferences.numberOfHistoryStates; try { // Ask to change any required preferences var msg = "" if ( rulerUnits != Units.PIXELS ) msg += " --> Ruler Units must be set to Pixels.\n"; if ( nonLinearHistory ) msg += " --> Nonlinear History must be disabled.\n"; if ( numberOfHistoryStates < 2 ) msg += " --> History States must be set to 2 or more.\n"; if ( msg != "" ) { if (confirm("ACR Calibrator L needs to change your Photoshop preferences:\n\n" + msg + "\n" + "Select YES to allow ACR Calibrator L to change your Photoshop preferences.\n" + "Present settings will be restored when the script exits.\n\n" + "Select NO to leave your preferences unchanged and exit ACR Calibrator L.\n\n" + "Warning: if ACR Calibrator L crashes unexpectedly, you will need to restore your\n" + "preferences manually.")) { preferences.rulerUnits = Units.PIXELS; preferences.nonLinearHistory = false; if ( numberOfHistoryStates < 2 ) preferences.numberOfHistoryStates = 2; } else { throw ("User aborted."); } } //----------------------------------- // build the options dialog menu // set defaults from global variables //----------------------------------- opt = new Options(); opt.buildMenu(); //-------------- // show the menu //-------------- retCd = opt.showMenu(); // returns 1 or 2 if (retCd == 2) { // user cancel throw("Aborted by user"); } //---------------------- // user changes accepted //---------------------- opt.rePop(); // update the global configuration variables // Initialize Debug Module var debug = new Debug(); debug.CreateLog("log", debug.GetActiveDocPath()+"/ACRcal-log.txt"); debug.EraseLog("log"); debug.CreateLog("plt", debug.GetActiveDocPath()+"/ACRcal-plot.txt"); debug.EraseLog("plt"); debug.CreateLog("dbg", debug.GetActiveDocPath()+"/ACRcal-debug.txt"); debug.EraseLog("dbg"); debug.CreateLog("cache", debug.GetActiveDocFullName()+"-ACRcal-cache.txt"); // Initialize ACR var acr = new Acr(); acr.Initialize(); // Initialize ColorChecker var cc = new ColorChecker(); cc.FindPatchesFromActiveDoc(); debug.AddStatusLine("ACR Calibrator L - Version 3.0 by Simon Tindemans"); debug.AddStatusLine("Derived from ACR Calibrator - Version Beta 3.8"); debug.AddStatusLine("Copyright (c) 2005-2006 Thomas Fors "); debug.AddStatusLine("Portions from ACR Calibrator Rags - Version 3.8"); debug.AddStatusLine("Copyright (c) 2005 Rags Gardner"); debug.AddStatusLine("--------------------------------------------------------"); debug.AddStatusLine(debug.GetActiveDocFullName()); debug.AddStatusLine("Corners: " + cc.patchCornerString); debug.AddStatusLine(""); debug.Refresh(); // Close open file after gathering the useful info from it acr.Close(); if (resetSliders) acr.ResetCalibration(); if (calMethod == "linearPreset") acr.Linearize(); else if (calMethod == "useXMP") acr.UsePreviousCurve(); cc.WhiteBalance(1); if (calMethod == "curveFit") { cc.GrayAdjust(); cc.WhiteBalance(2); } else cc.ExpAdjust(); if (calShadowTint == true) cc.ShadowTintAdjust(); cc.AllCalibration(1); var statString = "Done.\rSettings are valid in combination with the ACR profile [" + acr.profileVersion +"]."; statString += "\rElapsed Time: " + debug.GetElapsedMinutes() + " mins. AROPM: " + Math.round(acr.nOpens/debug.GetElapsedMinutes()*100)/100; //Calculate average and RMS DeltaE2000 values, both weighted and unweighted var err = 0; var errRMS = 0; var err_uw = 0; var err_uwRMS = 0; acr.Open(); var weightsum = 0; for (i=0 ; i<=23 ; i++) { err += weights[i]*cc.DeltaE2k(i); errRMS += weights[i]*Math.pow(cc.DeltaE2k(i),2); weightsum += weights[i]; } err /= weightsum; errRMS /= weightsum; errRMS = Math.sqrt(errRMS); DE_kL = 1; DE_kC = 1; DE_kH = 1; for (i=0 ; i<=23 ; i++) { err_uw += cc.DeltaE2k(i); err_uwRMS += Math.pow(cc.DeltaE2k(i),2); } err_uw /= 24; err_uwRMS /= 24; err_uwRMS = Math.sqrt(err_uwRMS); acr.Close(); // add error readings to the output string statString += "\rErrors (DeltaE2000):\rWeighted: Average: " + err + " RMS: " + errRMS + "\rUnweighted: Average: " + err_uw + " RMS: " + err_uwRMS; debug.DisplayStatus(statString); // Restore user's ruler units preferences.rulerUnits = rulerUnits; preferences.nonLinearHistory = nonLinearHistory; preferences.numberOfHistoryStates = numberOfHistoryStates; } catch (e) { // Restore user's ruler units preferences.rulerUnits = rulerUnits; preferences.nonLinearHistory = nonLinearHistory; preferences.numberOfHistoryStates = numberOfHistoryStates; alert(e); }