/*
CanonBanding.js v0.3.0
Joins previews into a new image.
Copyright (C) 2009 Georg Viehoever
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Changelog:
0.3.0: Fixing all channels, automatic workaround for PI1.5 and earlier. First tested with 1.5.0
0.2.0: Working processing as shown by Juan Conejero. Introduced
elements from Nial Saunder's Debayer script (beta version)
0.1.0: Initial version, modelled after Preview Allocator script provided with PixInsight
*/
// ======== #features ==============================================================
#feature-id Utilities > Canon Banding Reducton
#feature-info Attempts to reduce the horizontal banding plaguing some Canon DSLRs
\
This script allows you reduce the banding by equalizing horizontal background \
brightness
// #feature-icon Batch_CanonBandingReduction.xpm
// ========= # defines =============================================================
/// define for debugging output
#define DEBUGGING_MODE_ON false
/// somehow a true does not do any good
#define HAS_PREVIEW false
#define TITLE "CanonBanding"
#define VERSION "0.1.0"
#include <pjsr/StdButton.jsh>
#include <pjsr/StdIcon.jsh>
#include <pjsr/Sizer.jsh>
#include <pjsr/FrameStyle.jsh>
#include <pjsr/NumericControl.jsh>
// include constants
#include <pjsr/ImageOp.jsh>
#include <pjsr/SampleType.jsh>
/// Does the work using the params in data
function BandingEngine() {
// process the data
this.processImage = function(targetImage, amountValue) {
var targetHeight=targetImage.height;
var targetWidth=targetImage.width;
//rescale each row to median
// this represents the average background value
var medians=new Array;
/// true if img.apply(number, op) does not work, as in PI1.5.0 (build 492)
var hasApplyBug=(coreVersionBuild<=492)
for (var chan=0; chan<targetImage.numberOfChannels;++chan){
targetImage.selectedChannel=chan;
medians[chan]=targetImage.median();
} //for channel
if ( DEBUGGING_MODE_ON ){
console.writeln("Median=",medians);
} // if debugging
targetImage.initializeStatus("Processing rows", targetHeight);
var lineRect=new Rect(targetWidth,1);
for (var chan=0; chan<targetImage.numberOfChannels;++chan){
for (var row=0; row<targetHeight;++row) {
targetImage.selectedChannel=chan;
lineRect.moveTo(0,row);
targetImage.selectedRect=lineRect;
var lineMedian=targetImage.median();
var fixFactor=(medians[chan]-lineMedian)*amountValue;
if ( DEBUGGING_MODE_ON ){
console.writeln("Row, Channel, Median, fixFactor=",row,",",chan,",",lineMedian,",",fixFactor,",",ImageOp_Add);
} // if debugging
if (! hasApplyBug){
// This crashes in PI version 1.5 (and probably in 1.4.5, too - not tested)
targetImage.apply(fixFactor,ImageOp_Add);
}else{
// Workaround
for (var col=0;col<targetWidth;++col){
var sample=targetImage.sample(col,row,chan);
var fixedSample=sample+fixFactor;
targetImage.setSample(fixedSample,col,row,chan);
} //for col
} // if HAS_APPLY_BUG
targetImage.advanceStatus(1);
} //for row
} //for channel
targetImage.resetSelections();
targetImage.truncate();
targetImage.normalize();
}; //method processImage()
// do the actual work
this.doit=function(targetView,amountValue){
// Tell the core application that we are going to change this view.
// Without doing this, we'd have just read-only access to the view's image.
targetView.beginProcess();
var targetImage=targetView.image;
// convert image to floating point format, since int would not handle negatives or overflows
if ( targetImage.sampleType == SampleType_Integer ){
var wrk = new Image( targetImage.width, targetImage.height,
targetImage.numberOfChannels, targetImage.colorSpace,
(targetImage.bitsPerSample < 32) ? 32 : 64, SampleType_Real );
wrk.assign( targetImage );
this.processImage( wrk, amountValue );
targetImage.assign( wrk );
}else{
// work directly on targetImage
this.processImage(targetImage, amountValue);
} // if integer image
// end transaction
targetView.endProcess();
}; //function doit
} //class BandingEngine
/// dialog for getting params and starting process
function BandingDialog() {
this.__base__ = Dialog;
this.__base__();
//init values
this.previewWindow=null;
//default value fof slider
this.amountValue=1.0;
this.imageProcessed=false;
// ----- HELP LABEL
this.helpLabel = new Label (this);
this.helpLabel.frameStyle = FrameStyle_Box;
this.helpLabel.margin = 4;
this.helpLabel.wordWrapping = true;
this.helpLabel.useRichText = true;
this.helpLabel.text = "<b>" + TITLE + " v"+VERSION+"</b> — A script to remove " +
"Canon Banding from View.";
// ------ CHECKBOX
if ( HAS_PREVIEW){
this.previewCheckBox = new CheckBox( this );
this.previewCheckBox.text = "Display Preview";
this.previewCheckBox.toolTip ="<p>If this option is selected, the will display a preview</p>";
this.previewCheckBox.onCheck = function(checked){
if ( DEBUGGING_MODE_ON ){
console.writeln("previewCheckBox.onCheck()",checked);
} // if debugging
var dialog=this.dialog;
if(checked){
var window = ImageWindow.activeWindow;
if ( !window.isNull ){
if ( DEBUGGING_MODE_ON ){
console.writeln("previewCheckBox.onCheck(): Creating Preview");
} // if debugging
//dialog.previewWindow=new ImageWindow(window);
//this.previewWindow.bringToFront();
// this.previewWindow.show();
} // if active window exists
}else{ //checked
// FIXME somehow this just would not work...
if ( DEBUGGING_MODE_ON ){
console.writeln("previewCheckBox.onCheck(): dialog.previewWindow=",dialog.previewWindow);
} // if debugging
if (!dialog.previewWindow.isNull){
if ( DEBUGGING_MODE_ON ){
console.writeln("previewCheckBox.onCheck(): Deleting Preview");
} // if debugging
// dialog.previewWindow.forceClose();
// dialog.previewWindow=null;
} // if preview window exists
} // if checked
}; //onCheck()
} // if HAS_PREVIEW
// ----- SLIDER
var labelWidth1 = this.font.width( 'T' + "" );
this.amountControl=new NumericControl(this);
with (this.amountControl) {
label.text = "Amount:";
label.minWidth = labelWidth1;
setRange( 0, 4.0 );
slider.setRange( 0, 1000 );
slider.minWidth = 250;
setPrecision( 2 );
setValue( this.amountValue );
toolTip = "Define amount of correction";
onValueUpdated = function( value ) {
//console.writeln("processing in slider, amount=",value);
this.dialog.amountValue=value;
return;
var currentWindow = ImageWindow.activeWindow;
if (!currentWindow.isNull){
var currentView=currentWindow.currentView;
if (!currentView.isNull){
if (this.dialog.isImageProcessed){
// roll back
currentWindow.undo();
} // if roll back
var engine = new BandingEngine();
engine.doit(currentView, value);
this.dialog.isImageProcessed=true;
console.writeln("processed in slider");
} // if currentview
} //if currentWindow
}; //function onValueUpdated();
} //with amountControl
// ----- BUTTONS
this.ok_Button = new PushButton (this);
this.ok_Button.text = "OK";
// Do it
this.ok_Button.onClick = function() {
if ( DEBUGGING_MODE_ON ){
console.writeln("ok_Button.onClick()");
} // if debugging
this.dialog.ok();
};
this.cancel_Button = new PushButton (this);
this.cancel_Button.text = "Cancel";
this.cancel_Button.onClick = function() {
if ( DEBUGGING_MODE_ON ){
console.writeln("cancel_Button.onClick()");
} // if debugging
this.dialog.cancel();
};
// ----- PACK EVERYTHING INTO LAYOUT
this.buttons_Sizer = new HorizontalSizer;
this.buttons_Sizer.spacing = 4;
this.buttons_Sizer.addStretch();
this.buttons_Sizer.add (this.ok_Button);
this.buttons_Sizer.add (this.cancel_Button);
this.sizer = new VerticalSizer;
this.sizer.margin = 6;
this.sizer.spacing = 6;
this.sizer.add (this.helpLabel);
if ( HAS_PREVIEW){
this.sizer.addSpacing (4);
this.sizer.add (this.previewCheckBox);
} // if HAS_PREVIEW
this.sizer.addSpacing (4);
this.sizer.add (this.amountControl);
this.sizer.addSpacing (4);
this.sizer.add (this.buttons_Sizer);
this.windowTitle = TITLE + " Script v" + VERSION;
this.adjustToContents();
} //class BandingDialog
BandingDialog.prototype = new Dialog;
function main() {
// to display progress
console.show();
// allow stop
console.abortEnabled = true;
if ( DEBUGGING_MODE_ON ){
console.writeln("Version Info:", coreId,",",coreVersionBuild,",",coreVersionMajor,
",", coreVersionMinor,",",coreVersionRelease);
console.writeln ("Dialog opening");
} // if debug mode
// Dialog
var dialog = new BandingDialog();
// Global parameters.
for (;;) {
if (!dialog.execute()){
// cancel...
break;
}
var amountValue=dialog.amountValue;
if ( DEBUGGING_MODE_ON ){
console.writeln("main(): amountValue=",amountValue);
} // if debugging
var window = ImageWindow.activeWindow;
if ( window.isNull )
throw new Error( "No active image" );
if ( !window.currentView.isNull ){
var engine = new BandingEngine();
engine.doit(window.currentView, amountValue);
// Quit after successful execution./
break;
}else{
var msg = new MessageBox(
"<p>No action was selected. Do you want to continue?</p>",
TITLE, StdIcon_Warning, StdButton_Yes, StdButton_No );
if ( msg.execute() != StdButton_Yes )
break;
} //if !view.isNull
} //for
console.hide();
} //main()
main();