├── .gitattributes ├── .gitignore ├── AutoIntegrate.js ├── AutoIntegrateBanding.js ├── AutoIntegrateEngine.js ├── AutoIntegrateExclusionArea.js ├── AutoIntegrateGUI.js ├── AutoIntegrateGlobal.js ├── AutoIntegrateLDD.js ├── AutoIntegrateMetricsVisualizer.js ├── AutoIntegratePreview.js ├── AutoIntegrateUtil.js ├── CONTRIBUTING.md ├── README.md ├── hue.png └── startup.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bat 2 | .vscode 3 | .tmp.driveupload 4 | testing.txt 5 | TODO.txt 6 | -------------------------------------------------------------------------------- /AutoIntegrate.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Script to automate initial steps of image processing in PixInsight. 4 | 5 | For information check the page https://ruuth.xyz/AutoIntegrateInfo.html 6 | 7 | Script has a GUI interface where some processing options can be selected. 8 | 9 | In the end there will be integrated light files and automatically 10 | processed final image. Script accepts LRGB, color and narrowband files. 11 | It is also possible do only partial processing and continue manually. 12 | 13 | Clicking button Run on GUI all the following steps listed below are performed. 14 | 15 | LRGB files need to have keyword FILTER that has values for Luminance, Red, Green 16 | or Blue channels. A couple of variants are accepted like 'Red' or 'R'. 17 | If keyword FILTER is not found file name is use to resolve filter. If that 18 | fails then images are assumed to be color images. Also camera RAW files can be used. 19 | 20 | Script creates an AutoIntegrate.log file where details of the processing can be checked. 21 | 22 | NOTE! These steps may not be updated with recent changes. They do describe the basic 23 | processing but some details may have changed. To get a detailed workflow check the 24 | Flowchart buttons on the GUI. 25 | 26 | 27 | Manual processing 28 | ----------------- 29 | 30 | It is possible to rerun the script by clicking button AutoContinue with following steps 31 | if there are manually created images: 32 | - L_HT + RGB_HT 33 | LRGB image with HistogramTransformation already done, the script starts after step and . 34 | - RGB_HT 35 | Color (RGB) image with HistogramTransformation already done, the script starts after step . 36 | - Integration_L_GC + Integration_RGB_GC 37 | LRGB image background extracted, the script starts after step and . 38 | - Integration_RGB_GC 39 | Color (RGB) image background extracted, the script starts with after step . 40 | - Integration_L_GC + Integration_R_GC + Integration_G_GC + Integration_B_GC 41 | LRGB image background extracted before image integration, the script starts after step and 42 | . Automatic background extract is then skipped. 43 | - Integration_L + Integration_R + Integration_G + Integration_B + + Integration_H + Integration_S + Integration_O 44 | (L)RGB or narrowband image with integrated L,R,G,B;H,S,O images the script starts with step . 45 | 46 | Note that it is possible to run first automatic processing and then manually enhance some 47 | intermediate files and autocontinue there. 48 | - Crop integrated images and continue automatic processing using cropped images. 49 | - Run manual DBE or other gradient correction. 50 | 51 | Calibration steps 52 | ----------------- 53 | 54 | Calibration can run two basic workflows, one with bias files and one 55 | with flat dark files. There are some option to make small changes 56 | on those, 57 | 58 | 1. In each file page in GUI, select light, bias, dark, flat and/or flat dark frames. 59 | If only light are selected the calibration is skipped. Also bias, dark, flat or 60 | flat dark files may be not selected, so any one of those can be left out. Script 61 | tries to automatically detect different filters, or OSC/RAW files. 62 | 2. If bias files are present, those are integrated into master bias. Optionally a superbias 63 | file is created. 64 | 3. If dark files are present, those are optionally calibrated using master bias and then integrated 65 | into master dark. 66 | 4. If flat dark files are present, those are integrated into master flat dark. 67 | 5. If flat files are present, those are calibrated using master bias and master dark, or master 68 | flat dark, and then integrated into master flat. 69 | 6. Light files are then calibrated using master bias, master dark and master flat. 70 | 71 | Generic steps for all files 72 | --------------------------- 73 | 74 | 1. If light files not already selects, script opens a file dialog. On that select 75 | all *.fit (or other) files. LRGB, color, RAW and narrowband files can be used. 76 | 2. Optionally linear defect detection is run to find column and row defects. Defect information 77 | is used by the CosmeticCorrection. 78 | 3. By default run CosmeticCorrection for each file. 79 | 4. OSC/RAW files are debayered. 80 | 5. SubframeSelector is run on files to measure and generate SSWEIGHT for 81 | each file. Output is *_a.xisf files. 82 | 6. Files are scanned and the file with highest SSWEIGHT is selected as a 83 | reference. 84 | 7. StarAlign is run on all files and *_a_r.xisf files are 85 | generated. 86 | 8. Optionally there is LocalNormalization on all files. 87 | 88 | Steps with LRGB files 89 | --------------------- 90 | 91 | 1. ImageIntegration is run on LRGB files. Rejection method is chosen dynamically 92 | based on the number of image files, or specified by the user. 93 | After this step there are Integration_L, Integration_R, Integration_G and Integration_B images, 94 | or with narrowband Integration_H, Integration_S and Integration_O. 95 | 2. Optionally the Integration images and corresponding support images are cropped to the area 96 | contributed to by all images. 97 | 3. Optionally gradient correction in run on L image. 98 | 4. HistogramTransform is run on L image. 99 | 5. Stretched L image is stored as a mask unless user has a predefined mask named AutoMask. 100 | 6. Noise reduction is run on L image using a mask. 101 | 7. If GC_before_channel_combination is selected then gradient correction is run on each color channel (R,G,B). 102 | 103 | 8. By default LinearFit is run on RGB channels using L, R, G or B as a reference 104 | 9. If Channel noise reduction is non-zero then noise reduction is done separately 105 | for each R,G and B images using a mask. 106 | 10. ChannelCombination is run on Red, Green and Blue integrated images to 107 | create an RGB image. After that there is one L and one RGB image. 108 | 12. Optionally gradient correction is run on RGB image. 109 | 13. Color calibration is run on RGB image. Optionally 110 | BackgroundNeutralization is run before color calibration 111 | 14. HistogramTransform is run on RGB image. 112 | 15. Optionally TGVDenoise is run to reduce color noise. 113 | 16. Optionally a slight CurvesTransformation is run on RGB image to increase saturation. 114 | By default saturation is increased also when the image is still in a linear 115 | format. 116 | 17. LRGBCombination is run to generate final LRGB image. 117 | 118 | Steps with color files 119 | ---------------------- 120 | 121 | 1. ImageIntegration is run on color *_a_r.xisf files. 122 | Rejection method is chosen dynamically based on the number of image files. 123 | After this step there is Integration_RGB_color image. 124 | 3. Optionally gradient correction in run on RGB image. 125 | 4. Color calibration is run on RGB image. Optionally 126 | BackgroundNeutralization is run before color calibration 127 | 5. HistogramTransform is run on RGB image. 128 | 6. A mask is created from an extracted and stretched luminance channel. 129 | 7. MultiscaleLinearTransform is run on color RGB image to reduce noise. 130 | Mask is used to target noise reduction more on the background. 131 | 8. Optionally a slight CurvesTransformation is run on RGB image to increase saturation. 132 | 133 | Steps with narrowband files 134 | --------------------------- 135 | 136 | Steps for narrowband files are a bit similar to LRGB files but without L channel. 137 | - There is an option to choose how S, H and O files and mapped to R, G and B channels. 138 | - Color calibration is not run on narrowband images 139 | - Saturation default setting 1 does not increase saturation on narrowband images. 140 | - Linear fit for can be used for R, G or B channels. In that case script runs linked STF 141 | stretch. Default is to use unlinked STF stretch for narrowband files. 142 | - PixelMath expression can chosen from a list or edited manually for custom blending. 143 | PixelMath expressions can also include RGB channels. 144 | 145 | Narrowband to RGB mapping 146 | ------------------------- 147 | 148 | A special processing is used for narrowband to (L)RGB image mapping. It is used 149 | to enhance (L)RGB channels with narrowband data. It cannot be used without RGB filters. 150 | This mapping is similar to NBRGBCombination script in PixInsight or as described in 151 | Light Vortex Astronomy tutorial Combining LRGB with Narrowband. You can find more 152 | details on parameters from those sources. 153 | 154 | Common final steps for all images 155 | --------------------------------- 156 | 157 | 1. SCNR is run on to reduce green cast. 158 | 2. MultiscaleLinearTransform is run to sharpen the image. A mask is used to target 159 | sharpening more on the light parts of the image. 160 | 3. Extra windows are closed or minimized. 161 | 162 | Notes to self: 163 | - Start mask when set will target operation on stars. 164 | - Luminance mask when set will target operations on light parts of the image. 165 | - Mask from RangeSelection is opposite to luminance mask. So user must invert 166 | range_mask and rename it as AutoMask. 167 | 168 | 169 | Credits and Copyright notices 170 | ----------------------------- 171 | 172 | PixInsight scripts that come with the product were a great help. 173 | Web site Light Vortex Astronomy (http://www.lightvortexastronomy.com/) 174 | was a great place to find details and best practices when using PixInsight. 175 | For calibration another useful link is a PixInsight forum post 176 | "For beginners: Guide to PI's ImageCalibration": 177 | https://pixinsight.com/forum/index.php?threads/for-beginners-guide-to-pis-imagecalibration.11547/ 178 | 179 | Routines ApplyAutoSTF and applySTF are from PixInsight scripts that are 180 | distributed with PixInsight. 181 | 182 | Routines for Linear Defect Detection are from PixInsight scripts 183 | LinearDefectDetection.js and CommonFunctions.jsh that is distributed 184 | with PixInsight. 185 | 186 | This product is based on software from the PixInsight project, developed 187 | by Pleiades Astrophoto and its contributors (https://pixinsight.com/). 188 | 189 | Copyright (c) 2018-2025 Jarmo Ruuth. 190 | 191 | Crop to common area code 192 | 193 | Copyright (c) 2022 Jean-Marc Lugrin. 194 | 195 | Window name prefix and icon location code 196 | 197 | Copyright (c) 2021 rob pfile. 198 | 199 | PreviewControl module 200 | 201 | Copyright (C) 2013, Andres del Pozo 202 | 203 | The following copyright notice is for Linear Defect Detection 204 | 205 | Copyright (c) 2019 Vicent Peris (OAUV). All Rights Reserved. 206 | 207 | The following copyright notice is for routines ApplyAutoSTF and applySTF: 208 | 209 | Copyright (c) 2003-2020 Pleiades Astrophoto S.L. All Rights Reserved. 210 | 211 | The following condition apply for routines ApplyAutoSTF, applySTF and 212 | Linear Defect Detection: 213 | 214 | Redistribution and use in both source and binary forms, with or without 215 | modification, is permitted provided that the following conditions are met: 216 | 217 | 1. All redistributions of source code must retain the above copyright 218 | notice, this list of conditions and the following disclaimer. 219 | 220 | 2. All redistributions in binary form must reproduce the above copyright 221 | notice, this list of conditions and the following disclaimer in the 222 | documentation and/or other materials provided with the distribution. 223 | 224 | 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names 225 | of their contributors, may be used to endorse or promote products derived 226 | from this software without specific prior written permission. For written 227 | permission, please contact info@pixinsight.com. 228 | 229 | 4. All products derived from this software, in any form whatsoever, must 230 | reproduce the following acknowledgment in the end-user documentation 231 | and/or other materials provided with the product: 232 | 233 | "This product is based on software from the PixInsight project, developed 234 | by Pleiades Astrophoto and its contributors (https://pixinsight.com/)." 235 | 236 | Alternatively, if that is where third-party acknowledgments normally 237 | appear, this acknowledgment must be reproduced in the product itself. 238 | 239 | THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS 240 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 241 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 242 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS 243 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 244 | EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS 245 | INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE, 246 | DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 247 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 248 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 249 | POSSIBILITY OF SUCH DAMAGE. 250 | 251 | */ 252 | 253 | #ifndef TEST_AUTO_INTEGRATE 254 | "use strict;" 255 | #endif 256 | 257 | #feature-id AutoIntegrate : Batch Processing > AutoIntegrate 258 | 259 | #feature-info A script for running basic image processing workflow 260 | 261 | #include 262 | #include 263 | #include 264 | #include 265 | #include 266 | #include 267 | #include 268 | #include 269 | #include 270 | #include 271 | #include 272 | #include 273 | #include 274 | #include 275 | #include 276 | #include 277 | 278 | #include "AutoIntegrateGlobal.js" 279 | #include "AutoIntegrateUtil.js" 280 | 281 | #include "AutoIntegrateLDD.js" 282 | #include "AutoIntegrateBanding.js" 283 | #include "AutoIntegrateEngine.js" 284 | 285 | #include "AutoIntegratePreview.js" 286 | #include "AutoIntegrateGUI.js" 287 | 288 | function AutoIntegrate() { 289 | 290 | this.__base__ = Object; 291 | this.__base__(); 292 | 293 | var global = new AutoIntegrateGlobal(); 294 | var util = new AutoIntegrateUtil(global); 295 | var engine = new AutoIntegrateEngine(global, util); 296 | var gui = new AutoIntegrateGUI(global, util, engine); 297 | 298 | util.setGUI(gui); 299 | engine.setGUI(gui); 300 | 301 | var par = global.par; 302 | var ppar = global.ppar; 303 | 304 | /*************************************************************************** 305 | * 306 | * test utility functions 307 | * 308 | */ 309 | function init_pixinsight_version() 310 | { 311 | util.init_pixinsight_version(); 312 | } 313 | 314 | function readPersistentSettings() 315 | { 316 | if (global.do_not_read_settings) { 317 | console.writeln("Use default settings, do not read session settings from persistent module settings"); 318 | return; 319 | } 320 | // Read prefix info. We use new setting names to avoid conflict with 321 | // older global.columnCount/winPrefix names 322 | console.noteln("Read window prefix settings"); 323 | var tempSetting = Settings.read(SETTINGSKEY + "/prefixName", DataType_String); 324 | if (Settings.lastReadOK) { 325 | console.writeln("AutoIntegrate: Restored prefixName '" + tempSetting + "' from settings."); 326 | ppar.win_prefix = tempSetting; 327 | } 328 | if (par.start_with_empty_window_prefix.val) { 329 | ppar.win_prefix = ''; 330 | } 331 | var tempSetting = Settings.read(SETTINGSKEY + "/prefixArray", DataType_String); 332 | if (Settings.lastReadOK) { 333 | console.writeln("AutoIntegrate: Restored prefixArray '" + tempSetting + "' from settings."); 334 | ppar.prefixArray = JSON.parse(tempSetting); 335 | if (ppar.prefixArray.length > 0 && ppar.prefixArray[0].length == 2) { 336 | // We have old format prefix array without column position 337 | // Add column position as the first array element 338 | console.writeln("AutoIntegrate:converting old format prefix array " + JSON.stringify(ppar.prefixArray)); 339 | for (var i = 0; i < ppar.prefixArray.length; i++) { 340 | if (ppar.prefixArray[i] == null) { 341 | ppar.prefixArray[i] = [0, '-', 0]; 342 | } else if (ppar.prefixArray[i][0] == '-') { 343 | // add zero column position 344 | ppar.prefixArray[i].unshift(0); 345 | } else { 346 | // Used slot, add i as column position 347 | ppar.prefixArray[i].unshift(i); 348 | } 349 | } 350 | } 351 | gui.fix_win_prefix_array(); 352 | } 353 | var tempSetting = Settings.read(SETTINGSKEY + "/global.columnCount", DataType_Int32); 354 | if (Settings.lastReadOK) { 355 | console.writeln("AutoIntegrate: Restored global.columnCount '" + tempSetting + "' from settings."); 356 | ppar.userColumnCount = tempSetting; 357 | } 358 | if (!par.use_manual_icon_column.val) { 359 | ppar.userColumnCount = -1; 360 | } 361 | var tempSetting = Settings.read(SETTINGSKEY + "/lastDir", DataType_String); 362 | if (Settings.lastReadOK) { 363 | console.writeln("AutoIntegrate: Restored lastDir '" + tempSetting + "' from settings."); 364 | ppar.lastDir = tempSetting; 365 | } 366 | var tempSetting = Settings.read(SETTINGSKEY + "/savedVersion", DataType_String); 367 | if (Settings.lastReadOK) { 368 | console.writeln("AutoIntegrate: Restored savedVersion '" + tempSetting + "' from settings."); 369 | ppar.savedVersion = tempSetting; 370 | } 371 | 372 | var tempSetting = Settings.read(SETTINGSKEY + "/previewSettings", DataType_String); 373 | if (Settings.lastReadOK) { 374 | console.writeln("AutoIntegrate: Restored previewSettings '" + tempSetting + "' from settings."); 375 | var preview = JSON.parse(tempSetting); 376 | // Check that all settings are defined. When coming from older version 377 | // some values may be missing. Use defaults for missing values. 378 | if (preview.show_histogram == undefined) { 379 | preview.show_histogram = ppar.preview.show_histogram; 380 | } 381 | if (preview.histogram_height == undefined) { 382 | preview.histogram_height = ppar.preview.histogram_height; 383 | } 384 | if (preview.side_preview_width == undefined) { 385 | preview.side_preview_width = ppar.preview.side_preview_width; 386 | } 387 | if (preview.side_preview_height == undefined) { 388 | preview.side_preview_height = ppar.preview.side_preview_height; 389 | } 390 | if (preview.side_histogram_height == undefined) { 391 | preview.side_histogram_height = ppar.preview.side_histogram_height; 392 | } 393 | ppar.preview = preview; 394 | global.use_preview = ppar.preview.use_preview; 395 | } else { 396 | /* Read old style separate settings. */ 397 | var tempSetting = Settings.read(SETTINGSKEY + "/usePreview", DataType_Boolean); 398 | if (Settings.lastReadOK) { 399 | console.writeln("AutoIntegrate: Restored usePreview '" + tempSetting + "' from settings."); 400 | ppar.preview.use_preview = tempSetting; 401 | global.use_preview = tempSetting; 402 | } 403 | var tempSetting = Settings.read(SETTINGSKEY + "/sidePreviewVisible", DataType_Boolean); 404 | if (Settings.lastReadOK) { 405 | console.writeln("AutoIntegrate: Restored sidePreviewVisible '" + tempSetting + "' from settings."); 406 | ppar.preview.side_preview_visible = tempSetting; 407 | } 408 | /* Now we have preview size for each screen size. */ 409 | var tempSetting = Settings.read(SETTINGSKEY + "/previewWidth", DataType_Int32); 410 | if (Settings.lastReadOK) { 411 | console.writeln("AutoIntegrate: Restored previewWidth '" + tempSetting + "' from settings."); 412 | ppar.preview.preview_width = tempSetting; 413 | } 414 | var tempSetting = Settings.read(SETTINGSKEY + "/previewHeight", DataType_Int32); 415 | if (Settings.lastReadOK) { 416 | console.writeln("AutoIntegrate: Restored previewHeight '" + tempSetting + "' from settings."); 417 | ppar.preview.preview_height = tempSetting; 418 | } 419 | var tempSetting = Settings.read(SETTINGSKEY + "/useLargePreview", DataType_Boolean); 420 | if (Settings.lastReadOK) { 421 | console.writeln("AutoIntegrate: Restored useLargePreview '" + tempSetting + "' from settings."); 422 | ppar.preview.use_large_preview = tempSetting; 423 | } 424 | } 425 | 426 | var tempSetting = Settings.read(SETTINGSKEY + "/useSingleColumn", DataType_Boolean); 427 | if (Settings.lastReadOK) { 428 | console.writeln("AutoIntegrate: Restored useSingleColumn '" + tempSetting + "' from settings."); 429 | ppar.use_single_column = tempSetting; 430 | } 431 | var tempSetting = Settings.read(SETTINGSKEY + "/useMoreTabs ", DataType_Boolean); 432 | if (Settings.lastReadOK) { 433 | console.writeln("AutoIntegrate: Restored useMoreTabs '" + tempSetting + "' from settings."); 434 | ppar.use_more_tabs = tempSetting; 435 | } 436 | if (ppar.use_single_column) { 437 | ppar.use_more_tabs = false; 438 | } 439 | var tempSetting = Settings.read(SETTINGSKEY + "/filesInTab", DataType_Boolean); 440 | if (Settings.lastReadOK) { 441 | console.writeln("AutoIntegrate: Restored filesInTab '" + tempSetting + "' from settings."); 442 | ppar.files_in_tab = tempSetting; 443 | } 444 | var tempSetting = Settings.read(SETTINGSKEY + "/showStartupImage ", DataType_Boolean); 445 | if (Settings.lastReadOK) { 446 | console.writeln("AutoIntegrate: Restored showStartupImage '" + tempSetting + "' from settings."); 447 | ppar.show_startup_image = tempSetting; 448 | } 449 | var tempSetting = Settings.read(SETTINGSKEY + "/startupImageName", DataType_String); 450 | if (Settings.lastReadOK) { 451 | console.writeln("AutoIntegrate: Restored startupImageName '" + tempSetting + "' from settings."); 452 | ppar.startup_image_name = tempSetting; 453 | } 454 | } 455 | 456 | function readOneParameterFromProcessIcon(name, type) 457 | { 458 | var val = null; 459 | name = util.mapBadChars(name); 460 | if (Parameters.has(name)) { 461 | switch (type) { 462 | case 'S': 463 | var val = Parameters.getString(name); 464 | console.writeln(name + "=" + val); 465 | break; 466 | case 'B': 467 | var val = Parameters.getBoolean(name); 468 | console.writeln(name + "=" + val); 469 | break; 470 | case 'I': 471 | var val = Parameters.getInteger(name); 472 | console.writeln(name + "=" + val); 473 | break; 474 | case 'R': 475 | var val = Parameters.getReal(name); 476 | console.writeln(name + "=" + val); 477 | break; 478 | default: 479 | util.throwFatalError("Unknown type '" + type + '" for parameter ' + name); 480 | break; 481 | } 482 | } 483 | return val; 484 | } 485 | 486 | // Read default parameters from process icon 487 | function readParametersFromProcessIcon() 488 | { 489 | if (global.do_not_read_settings) { 490 | console.writeln("Use default settings, do not read parameter values from process icon"); 491 | return; 492 | } 493 | console.writeln("readParametersFromProcessIcon"); 494 | for (let x in par) { 495 | var param = par[x]; 496 | var val = readOneParameterFromProcessIcon(param.name, param.type); 497 | if (val == null && param.oldname != undefined) { 498 | val = readOneParameterFromProcessIcon(param.oldname, param.type); 499 | } 500 | if (val != null) { 501 | param.val = val; 502 | } 503 | } 504 | } 505 | 506 | function readOneParameterFromPersistentModuleSettings(name, type) 507 | { 508 | name = SETTINGSKEY + '/' + util.mapBadChars(name); 509 | switch (type) { 510 | case 'S': 511 | var tempSetting = Settings.read(name, DataType_String); 512 | break; 513 | case 'B': 514 | var tempSetting = Settings.read(name, DataType_Boolean); 515 | break; 516 | case 'I': 517 | var tempSetting = Settings.read(name, DataType_Int32); 518 | break; 519 | case 'R': 520 | var tempSetting = Settings.read(name, DataType_Real32); 521 | break; 522 | default: 523 | util.throwFatalError("Unknown type '" + type + '" for parameter ' + name); 524 | break; 525 | } 526 | if (Settings.lastReadOK) { 527 | console.writeln("AutoIntegrate: read from settings " + name + "=" + tempSetting); 528 | return tempSetting; 529 | } else { 530 | return null; 531 | } 532 | } 533 | 534 | // Read default parameters from persistent module settings 535 | function readParametersFromPersistentModuleSettings() 536 | { 537 | if (global.do_not_read_settings) { 538 | console.writeln("Use default settings, do not read parameter values from persistent module settings"); 539 | return; 540 | } 541 | if (!global.ai_use_persistent_module_settings) { 542 | console.writeln("skip readParametersFromPersistentModuleSettings"); 543 | return; 544 | } 545 | console.writeln("readParametersFromPersistentModuleSettings"); 546 | for (let x in par) { 547 | var param = par[x]; 548 | var val = readOneParameterFromPersistentModuleSettings(param.name, param.type); 549 | if (val == null && param.oldname != undefined) { 550 | val = readOneParameterFromPersistentModuleSettings(param.oldname, param.type); 551 | } 552 | if (val != null) { 553 | param.val = val; 554 | } 555 | } 556 | } 557 | 558 | this.test_initdebug = function() 559 | { 560 | global.testmode = true; 561 | global.par.debug.val = true; 562 | global.ai_use_persistent_module_settings = false; // do not read defaults from persistent module settings 563 | 564 | } 565 | 566 | this.test_initialize = function() 567 | { 568 | console.writeln("test_initialize"); 569 | 570 | init_pixinsight_version(); 571 | 572 | global.do_not_write_settings = true; 573 | 574 | util.setDefaultDirs(); 575 | 576 | // Initialize ppar to the default values they have when the script is started 577 | ppar.win_prefix = ''; 578 | ppar.prefixArray = []; 579 | ppar.userColumnCount = -1; 580 | ppar.lastDir = ''; 581 | 582 | // Hopefully remove the prefixes of a previous run 583 | util.fixAllWindowArrays(ppar.win_prefix); 584 | 585 | // Reset the parameters to the default they would have when the program is loaded 586 | util.setParameterDefaults(); 587 | 588 | console.writeln("test_initialize done"); 589 | } 590 | 591 | this.test_autosetup = function(autosetup_path) 592 | { 593 | console.writeln("test_autosetup"); 594 | 595 | var pagearray = util.readJsonFile(autosetup_path, false); 596 | 597 | for (var i = 0; i < pagearray.length; i++) { 598 | if (pagearray[i] != null) { 599 | gui.addFilesToTreeBox(this.dialog, i, pagearray[i]); 600 | } 601 | } 602 | gui.updateInfoLabel(this.dialog); 603 | 604 | console.writeln("test_autosetup done"); 605 | } 606 | 607 | this.test_getpar = function() 608 | { 609 | return par; 610 | } 611 | 612 | this.test_getppar = function() 613 | { 614 | return ppar; 615 | } 616 | 617 | this.test_gui = function() 618 | { 619 | return gui; 620 | } 621 | 622 | this.get_run_results = function() 623 | { 624 | return global.run_results; 625 | } 626 | 627 | this.get_autointegrate_version = function() 628 | { 629 | return global.autointegrate_version; 630 | } 631 | 632 | this.set_outputRootDir = function(dir) 633 | { 634 | global.outputRootDir = dir; 635 | } 636 | 637 | this.set_dialog = function(dialog) 638 | { 639 | this.dialog = dialog; 640 | } 641 | 642 | this.openImageWindowFromFile = function(name) 643 | { 644 | return util.openImageWindowFromFile(name); 645 | } 646 | 647 | /*************************************************************************** 648 | * 649 | * autointegrate_main 650 | * 651 | */ 652 | this.autointegrate_main = function() 653 | { 654 | console.writeln("autointegrate_main"); 655 | try { 656 | /* Check command line arguments. Arguments can be given by starting the script from 657 | * the command line in the Process Console window. Arguments are given using syntax: 658 | * -a="value" 659 | * For example: 660 | * run -a="do_not_read_settings" -a="do_not_write_settings" --execute-mode=auto "C:/Users/jarmo_000/GitHub/AutoIntegrate/AutoIntegrate.js" 661 | * You can find the start command line by checking the Process Console window after starting 662 | * the scrip. 663 | */ 664 | for (let i = 0; i < jsArguments.length; i++) { 665 | if (jsArguments[i] == "do_not_read_settings") { 666 | console.writeln("Found do_not_read_settings argument, no parameters are read from persistent module settings or from icon."); 667 | global.do_not_read_settings = true; 668 | } 669 | if (jsArguments[i] == "do_not_write_settings") { 670 | console.writeln("Found do_not_write_settings argument, no parameters are written to persistent module settings."); 671 | global.do_not_write_settings = true; 672 | } 673 | } 674 | 675 | util.setDefaultDirs(); 676 | 677 | 678 | if (Parameters.isGlobalTarget || Parameters.isViewTarget) { 679 | // 1. Read parameters saved to process icon, these overwrite default settings 680 | // read default parameters from saved settings/process icon 681 | console.noteln("Read process icon settings"); 682 | try { 683 | readParametersFromProcessIcon(); 684 | } catch(err) { 685 | console.writeln("Error reading parameters from process icon: " + err); 686 | } 687 | } else { 688 | // 2. Read saved parameters from persistent module settings 689 | console.noteln("Read persistent module settings"); 690 | readParametersFromPersistentModuleSettings(); 691 | } 692 | 693 | if (global.ai_use_persistent_module_settings) { 694 | // 3. Read persistent module settings that are temporary work values 695 | readPersistentSettings(); 696 | } else { 697 | console.noteln("Skip reading persistent settings"); 698 | } 699 | 700 | util.fixAllWindowArrays(ppar.win_prefix); 701 | 702 | init_pixinsight_version(); 703 | 704 | console.criticalln(" _____ __ .___ __ __ "); 705 | console.criticalln(" \/ _ __ ___\/ |_ ____ | | _____\/ |_ ____ ________________ _\/ |_ ____ "); 706 | console.criticalln(" \/ \/_ | | __ \/ _ | |\/ __ \/ __ \/ ___ _ __ __ __ \/ __ "); 707 | console.warningln("\/ | | \/| | ( (_) ) | | | ___\/\/ \/_\/ > | \/\/ __ | | ___\/ "); 708 | console.warningln(" ____|__ \/____\/ |__| ____\/|___|___| \/__| ___ >___ \/|__| (____ \/__| ___ > "); 709 | console.warningln(" \/ \/ \/_____\/ \/ \/ "); 710 | 711 | console.noteln("======================================================"); 712 | console.noteln("To enable automatic updates add the following link to "); 713 | console.noteln("the PixInsight update repository: "); 714 | console.noteln("https://ruuth.xyz/autointegrate/ "); 715 | console.noteln("======================================================"); 716 | console.noteln("For more information visit the following links:"); 717 | console.noteln("Web site: " + global.autointegrateinfo_link); 718 | console.noteln("Discussion forums: https://forums.ruuth.xyz"); 719 | console.noteln("Discord: https://discord.gg/baqMqmKS3N"); 720 | console.noteln("======================================================"); 721 | console.noteln(global.autointegrate_version + ", PixInsight v" + global.pixinsight_version_str + ' (' + global.pixinsight_version_num + ')'); 722 | console.noteln("======================================================"); 723 | if (global.autointegrate_version_info.length > 0) { 724 | for (var i = 0; i < global.autointegrate_version_info.length; i++) { 725 | console.noteln(global.autointegrate_version_info[i]); 726 | } 727 | console.noteln("======================================================"); 728 | } 729 | if (global.pixinsight_version_num < 1080810) { 730 | var old_default = 'Generic'; 731 | if (par.use_weight.val == par.use_weight.def 732 | && par.use_weight.def != old_default) 733 | { 734 | console.noteln("PixInsight version is older than 1.8.8-10, using " + old_default + " instead of " + 735 | par.use_weight.def + " for " + par.use_weight.name); 736 | par.use_weight.val = old_default; 737 | par.use_weight.def = old_default; 738 | } 739 | } 740 | if (global.pixinsight_version_num >= 1080902 && global.pixinsight_build_num >= 1601) { 741 | // We have GradientCorrection process available 742 | global.is_gc_process = true; 743 | } else { 744 | // Old versions do not have GradientCorrection process 745 | par.use_abe.val = true; 746 | par.use_abe.def = true; 747 | global.is_gc_process = false; 748 | } 749 | if (global.pixinsight_version_num >= 1090000) { 750 | global.is_mgc_process = true; 751 | } else { 752 | global.is_mgc_process = false; 753 | } 754 | } 755 | catch (x) { 756 | console.writeln( x ); 757 | } 758 | 759 | this.dialog = new gui.AutoIntegrateDialog(); 760 | global.dialog = this.dialog; 761 | this.dialog.execute(); 762 | } 763 | 764 | } // AutoIntegrate wrapper end 765 | 766 | AutoIntegrate.prototype = new Object; 767 | 768 | function main() 769 | { 770 | var autointegrate = new AutoIntegrate(); 771 | 772 | autointegrate.autointegrate_main(); 773 | 774 | autointegrate = null; 775 | } 776 | 777 | // Disable execution of main if the script is included as part of a test 778 | #ifndef TEST_AUTO_INTEGRATE 779 | main(); 780 | #endif 781 | -------------------------------------------------------------------------------- /AutoIntegrateBanding.js: -------------------------------------------------------------------------------- 1 | /* 2 | CanonBandingReduction.js v0.9.1 3 | Reduces Banding in Canon DSLR images. 4 | 5 | Copied from CanonBandingReduction.js script. 6 | Renamed by Jarmo Ruuth for AutoIntegrate script. 7 | 8 | The problem has been discussed for example in http://tech.groups.yahoo.com/group/digital_astro/message/126102, 9 | cannot be fixed by image normalization with flats or darks, is not caused by problems in power supplies or 10 | electromagnetic noise, and affects different camera exemplars to differing extents. 11 | 12 | The general idea is to fix this by flattening the background looking at the median of each row. Additional 13 | tweaks try to avoid overcorrection caused by bright sections of the image. See BandingEngine.doit() for the details. 14 | 15 | This works very nicely for my Canon EOS40D. Let me know how it works for yours. Feel free to improve the script! 16 | 17 | Copyright (C) 2009-2013 Georg Viehoever 18 | 19 | This program is free software: you can redistribute it and/or modify it 20 | under the terms of the GNU General Public License as published by the 21 | Free Software Foundation, version 3 of the License. 22 | 23 | This program is distributed in the hope that it will be useful, but WITHOUT 24 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 25 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 26 | more details. 27 | 28 | You should have received a copy of the GNU General Public License along with 29 | this program. If not, see . 30 | */ 31 | 32 | /// @class Does the work using the params in data. 33 | /// 34 | /// GUI functions are in a separate class, so this engine 35 | /// can be used on its own. Use the different set..() function 36 | /// to set the parameters, because these also schedule necessary recomputations. 37 | /// Use getResult() to trigger processing and retrieve 38 | /// result 39 | function AutoIntegrateBandingEngine() { 40 | 41 | var DEBUGGING_MODE_ON = true; 42 | 43 | // init members 44 | this.targetImage=null; // image to which operation is done 45 | this.convertedTargetImage=null; // target image converted to float or doubles 46 | this.fixupImage=null; // image that contains the fix values, assuming dAmount 1.0 47 | this.resultImage=null; //result image 48 | 49 | //numeric params with their defaults 50 | this.dAmount=1.0; // image is fixed by adding fixImage*dAmount 51 | this.bDoHighlightProtect=false; // toggle for protection from bright pixels 52 | this.dSigma=1.0; //factor for sigma in hihglight protection 53 | 54 | /// retrieve settings from previous runs, stored in Parameters 55 | this.importParameters = function() { 56 | if ( Parameters.has( "amount" ) ) 57 | this.dAmount = Parameters.getReal( "amount" ); 58 | if ( Parameters.has( "highlightProtect" ) ) 59 | this.bDoHighlightProtect = Parameters.getBoolean( "highlightProtect" ); 60 | if ( Parameters.has( "sigma" ) ) 61 | this.dSigma = Parameters.getReal( "sigma" ); 62 | }; 63 | 64 | /// Store current settings for use with later runs. 65 | this.exportParameters = function() { 66 | Parameters.set( "amount", this.dAmount ); 67 | Parameters.set( "highlightProtect", this.bDoHighlightProtect ); 68 | Parameters.set( "sigma", this.dSigma ); 69 | }; 70 | 71 | // flags to show if something needs to be recomputed. Managed by set...() and do...() functions 72 | this.bRedoConvert=true; 73 | this.bRedoStatistics=true; 74 | this.bRedoResult=true; 75 | 76 | /// function to set new target image 77 | this.setTargetImage=function(targetImage){ 78 | if (this.targetImage!=targetImage){ 79 | console.writeln("Setting targetImage=",targetImage); 80 | this.targetImage=targetImage; 81 | this.bRedoConvert=true; 82 | this.bRedoStatistics=true; 83 | this.bRedoResult=true; 84 | } //if setting changed 85 | }; //setTargetImage() 86 | 87 | /// function to set new amount value 88 | this.setAmount=function(amountValue){ 89 | if (this.dAmount!=amountValue){ 90 | this.dAmount=amountValue; 91 | this.bRedoResult=true; 92 | } //if setting changed 93 | }; //setAmount() 94 | 95 | /// function to set highlightProtect mode 96 | this.setHighlightProtect=function(doHighlightProtect,sigmaValue){ 97 | if ( DEBUGGING_MODE_ON ){ 98 | console.writeln("SetHighlightProtect=",doHighlightProtect,sigmaValue); 99 | } 100 | if((this.bDoHighlightProtect!=doHighlightProtect)|| 101 | (doHighlightProtect &&(this.dSigma!=sigmaValue))){ 102 | //something relevant changed 103 | this.bDoHighlightProtect=doHighlightProtect; 104 | this.dSigma=sigmaValue; 105 | this.bRedoStatistics=true; 106 | this.bRedoResult=true; 107 | } //if changed 108 | }; //setHighlightProtect() 109 | 110 | /// set status function that is called by time consuming operations for progress reporting and cancel query 111 | /// 112 | /// Operations include the image statistics and the actual correction. Can be used for progress reporting and canceling long operations. 113 | /// @param statusFunction is a function that receives a string (that can be displayed somewhere) 114 | /// and that returns a boolean. The boolean is true if operation can continue, if false 115 | /// the opertion is aborted ASAP. bForceUpdate can be used to force a GUI update. 116 | /// If ==null, a default function doing nothing is set. 117 | this.setStatusFunction=function(statusFunction) { 118 | if (statusFunction==null){ 119 | // set default doing nothing 120 | this.statusFunction=function(statusString,bForceUpdate) { 121 | if ( DEBUGGING_MODE_ON ){ 122 | console.writeln("statusFunction, string=",statusString, "bForceUpdate=",bForceUpdate); 123 | } 124 | return true; 125 | } //statusFunction() 126 | }else{ 127 | this.statusFunction=statusFunction; 128 | } 129 | }; //function setStatusFunction() 130 | 131 | // set default status function 132 | this.setStatusFunction(null); 133 | 134 | /// function converts target image to float type if necessary 135 | this.doConvertImage=function(){ 136 | if ( DEBUGGING_MODE_ON ){ 137 | console.writeln("BandingEngine.doConvertImage(), targetImage=",this.targetImage); 138 | } 139 | if(!this.statusFunction("Converting image to float type",true)) return; 140 | 141 | if (this.bRedoConvert){ 142 | if (this.targetImage.isNull){ 143 | if ( DEBUGGING_MODE_ON ){ 144 | console.writeln("doConvertImage(). targetImage is null"); 145 | } 146 | this.convertedImage=null; 147 | }else{ 148 | // convert image to floating point format, since int would not handle negatives or overflows 149 | if ( this.targetImage.sampleType == SampleType_Integer ){ 150 | this.convertedImage = new Image( this.targetImage.width, this.targetImage.height, 151 | this.targetImage.numberOfChannels, this.targetImage.colorSpace, 152 | (this.targetImage.bitsPerSample < 32) ? 32 : 64, SampleType_Real ); 153 | this.targetImage.resetSelections(); 154 | this.convertedImage.resetSelections() 155 | this.convertedImage.assign( this.targetImage ); 156 | }else{ 157 | // no conversion required 158 | this.targetImage.resetSelections(); 159 | this.convertedImage=this.targetImage; 160 | } //if conversion necessary 161 | } //if target=null 162 | this.bRedoConvert=false; 163 | this.statusFunction("Conversion done",true); 164 | } // if redoConvert 165 | if ( DEBUGGING_MODE_ON ){ 166 | console.writeln("BandingEngine.doConvertImage() done"); 167 | } 168 | }; //convertImage() 169 | 170 | /// do statistics and medians if necessary. Create fixImage. 171 | this.doStatistics=function(){ 172 | if ( DEBUGGING_MODE_ON ){ 173 | var now=Date.now(); 174 | console.writeln("BandingEngine.doStatistics()"); 175 | } 176 | if (this.bRedoStatistics){ 177 | this.doConvertImage(); 178 | var targetImage=this.convertedImage; 179 | if (targetImage.isNull) return; 180 | 181 | this.fixImage=new Image( targetImage.width, targetImage.height, 182 | targetImage.numberOfChannels, targetImage.colorSpace, 183 | targetImage.bitsPerSample, SampleType_Real ); 184 | var fixImage=this.fixImage; 185 | targetImage.resetSelections(); 186 | fixImage.resetSelections(); 187 | 188 | var targetHeight=targetImage.height; 189 | var targetWidth=targetImage.width; 190 | // for each channel, determine global statistics (average, deviation). 191 | // for each line, determine line average (bounded by possible highlight protection). 192 | for (var chan=0; chan 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | function AutoIntegrateExclusionArea(global) 18 | { 19 | 20 | this.__base__ = Object; 21 | this.__base__(); 22 | 23 | var dialog; 24 | 25 | // Global variables 26 | var exclusionAreas = []; // Array of arrays, each containing points for a polygon 27 | var activePolygon = []; // Current polygon being drawn 28 | var isDrawing = false; 29 | var targetView = null; 30 | var targetWindow = null; 31 | var previewControl = null; 32 | var title = "AutoIntegrate Exclusion Area"; 33 | var scale = 1.0; 34 | 35 | // Main function - creates the script dialog 36 | function ExclusionAreaDialog() { 37 | this.__base__ = Dialog; 38 | this.__base__(); 39 | 40 | var labelWidth = this.font.width("Exclusion areas: 000") + 4; 41 | 42 | // UI components 43 | this.helpLabel = new Label(this); 44 | // this.helpLabel.text = "Click on the image to define polygon vertices. Double-click to close polygon."; 45 | this.helpLabel.text = "Click on the image to define polygon vertices."; 46 | this.helpLabel.textAlignment = TextAlign_Left|TextAlign_VertCenter; 47 | 48 | this.targetImage_Label = new Label(this); 49 | this.targetImage_Label.text = "Target image:"; 50 | this.targetImage_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 51 | this.targetImage_Label.minWidth = labelWidth; 52 | 53 | this.targetImage_Info = new Label(this); 54 | 55 | if (targetView && targetWindow) { 56 | this.targetImage_Info.text = targetWindow.mainView.id; 57 | setPreviewForView(); 58 | } else { 59 | this.targetImage_Info.text = "No active image"; 60 | } 61 | 62 | this.startDrawing_Button = new PushButton(this); 63 | var startDrawing_Button = this.startDrawing_Button; 64 | this.startDrawing_Button.text = "Start Drawing"; 65 | this.startDrawing_Button.icon = this.scaledResource(":/icons/window-new.png"); 66 | this.startDrawing_Button.onClick = function() { 67 | if (!targetView) { 68 | (new MessageBox("No active image. Please open an image first.", title, StdIcon_Error)).execute(); 69 | return; 70 | } 71 | 72 | activePolygon = []; 73 | lastMousePos = null; 74 | isDrawing = true; 75 | 76 | // Enable real-time preview to show drawing 77 | installPolygonHandler(); 78 | 79 | startDrawing_Button.enabled = false; 80 | finishDrawing_Button.enabled = true; 81 | cancelDrawing_Button.enabled = true; 82 | }; 83 | 84 | this.finishDrawing_Button = new PushButton(this); 85 | var finishDrawing_Button = this.finishDrawing_Button; 86 | this.finishDrawing_Button.text = "Finish Current Polygon"; 87 | this.finishDrawing_Button.icon = this.scaledResource(":/icons/ok.png"); 88 | this.finishDrawing_Button.enabled = false; 89 | this.finishDrawing_Button.onClick = function() { 90 | finishPolygon(); 91 | }; 92 | 93 | this.cancelDrawing_Button = new PushButton(this); 94 | var cancelDrawing_Button = this.cancelDrawing_Button; 95 | this.cancelDrawing_Button.text = "Cancel Drawing"; 96 | this.cancelDrawing_Button.icon = this.scaledResource(":/icons/cancel.png"); 97 | this.cancelDrawing_Button.enabled = false; 98 | this.cancelDrawing_Button.onClick = function() { 99 | activePolygon = []; 100 | isDrawing = false; 101 | uninstallPolygonHandler(); 102 | updatePreview(); 103 | 104 | this.dialog.startDrawing_Button.enabled = true; 105 | this.dialog.finishDrawing_Button.enabled = false; 106 | this.dialog.cancelDrawing_Button.enabled = false; 107 | }; 108 | 109 | this.exclusionCount_Label = new Label(this); 110 | this.exclusionCount_Label.text = "Exclusion areas: " + exclusionAreas.length; 111 | this.exclusionCount_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 112 | 113 | this.preview_Control = new Control(this); 114 | 115 | // Make the preview match the image dimensions 116 | // Calculate a suitable preview size that maintains aspect ratio 117 | var imgWidth = targetView.image.width; 118 | var imgHeight = targetView.image.height; 119 | 120 | // Set the preview size 121 | this.preview_Control.setFixedSize(Math.round(imgWidth * scale), Math.round(imgHeight * scale)); 122 | 123 | this.preview_Control.backgroundcolor = 0xFF000000; 124 | this.preview_Control.toolTip = "Preview of exclusion areas"; 125 | this.preview_Control.onPaint = function() { 126 | drawPreview(this); 127 | }; 128 | previewControl = this.preview_Control; 129 | 130 | this.clearAll_Button = new PushButton(this); 131 | this.clearAll_Button.text = "Clear All Areas"; 132 | this.clearAll_Button.icon = this.scaledResource(":/icons/delete.png"); 133 | this.clearAll_Button.onClick = function() { 134 | if (exclusionAreas.length > 0) { 135 | if ((new MessageBox("Do you really want to delete all exclusion areas?", 136 | title, StdIcon_Warning, StdButton_Yes, StdButton_No)).execute() == StdButton_Yes) { 137 | exclusionAreas = []; 138 | updateExclusionCount(); 139 | updatePreview(); 140 | } 141 | } 142 | }; 143 | 144 | this.dialog_ok_Button = new PushButton(this); 145 | this.dialog_ok_Button.text = "OK"; 146 | this.dialog_ok_Button.icon = this.scaledResource(":/icons/ok.png"); 147 | this.dialog_ok_Button.onClick = function() { 148 | this.dialog.ok(); 149 | }; 150 | this.dialog_cancel_Button = new PushButton(this); 151 | this.dialog_cancel_Button.text = "Cancel"; 152 | this.dialog_cancel_Button.icon = this.scaledResource(":/icons/cancel.png"); 153 | this.dialog_cancel_Button.onClick = function() { 154 | this.dialog.cancel(); 155 | }; 156 | 157 | // Dialog layout 158 | this.sizer = new VerticalSizer; 159 | this.sizer.margin = 8; 160 | this.sizer.spacing = 6; 161 | 162 | // Help text 163 | this.sizer.add(this.helpLabel); 164 | this.sizer.addSpacing(4); 165 | 166 | // Target selection 167 | var targetSizer = new HorizontalSizer; 168 | targetSizer.spacing = 4; 169 | targetSizer.add(this.targetImage_Label); 170 | targetSizer.add(this.targetImage_Info, 100); 171 | this.sizer.add(targetSizer); 172 | this.sizer.addSpacing(8); 173 | 174 | // Drawing controls 175 | var drawingSizer = new HorizontalSizer; 176 | drawingSizer.spacing = 4; 177 | drawingSizer.add(this.startDrawing_Button); 178 | drawingSizer.add(this.finishDrawing_Button); 179 | drawingSizer.add(this.cancelDrawing_Button); 180 | drawingSizer.addStretch(); 181 | drawingSizer.add(this.exclusionCount_Label); 182 | this.sizer.add(drawingSizer); 183 | this.sizer.addSpacing(4); 184 | 185 | // Preview 186 | var previewSizer = new HorizontalSizer; 187 | previewSizer.add(this.preview_Control, 100); 188 | this.sizer.add(previewSizer, 100); 189 | this.sizer.addSpacing(4); 190 | 191 | // Area management 192 | var managementSizer = new HorizontalSizer; 193 | managementSizer.spacing = 4; 194 | managementSizer.add(this.clearAll_Button); 195 | managementSizer.addStretch(); 196 | managementSizer.add(this.dialog_ok_Button); 197 | managementSizer.add(this.dialog_cancel_Button); 198 | this.sizer.add(managementSizer); 199 | 200 | this.windowTitle = title; 201 | this.adjustToContents(); 202 | } 203 | 204 | // Inherit all properties and methods from the Dialog object 205 | ExclusionAreaDialog.prototype = new Dialog; 206 | 207 | // Helper function to draw the preview 208 | function drawPreview(control) { 209 | if (!targetView) return; 210 | 211 | // console.writeln("Drawing preview..."); 212 | 213 | var bitmap = targetView.image.render().scaledTo(control.width, control.height); 214 | 215 | var g = new Graphics(bitmap); 216 | 217 | var offsetX = control.width / 2; 218 | var offsetY = control.height / 2; 219 | 220 | // Draw existing exclusion areas 221 | g.pen = new Pen(0xFFFF6600, 1); 222 | 223 | for (var i = 0; i < exclusionAreas.length; i++) { 224 | var polygon = exclusionAreas[i]; 225 | if (polygon.length > 0) { 226 | for (var j = 1; j < polygon.length; j++) { 227 | // console.writeln("Drawing line: " + polygon[j-1].x + ", " + polygon[j-1].y + " to " + polygon[j].x + ", " + polygon[j].y); 228 | g.drawLine(polygon[j-1].x, polygon[j-1].y, polygon[j].x, polygon[j].y); 229 | } 230 | } 231 | } 232 | 233 | // Draw the active polygon being created 234 | if (activePolygon.length > 0) { 235 | g.pen = new Pen(0xFFFFFF00, 1); 236 | 237 | for (var j = 1; j < activePolygon.length; j++) { 238 | // console.writeln("Drawing active line: " + activePolygon[j-1].x + ", " + activePolygon[j-1].y + " to " + activePolygon[j].x + ", " + activePolygon[j].y); 239 | g.drawLine(activePolygon[j-1].x, activePolygon[j-1].y, activePolygon[j].x, activePolygon[j].y); 240 | } 241 | 242 | // Draw a line to the current mouse position if we're drawing 243 | if (0 && isDrawing && lastMousePos) { 244 | let last = activePolygon[activePolygon.length - 1]; 245 | let mousePos = lastMousePos; 246 | // console.writeln("Drawing line to last mouse position: " + last.x + ", " + last.y + " to " + mousePos.x + ", " + mousePos.y); 247 | g.drawLine(last.x, last.y, mousePos.x, mousePos.y); 248 | } 249 | } 250 | 251 | g.end(); 252 | 253 | var graphics = new Graphics(control); 254 | //graphics.drawBitmap(offsetX, offsetY, bitmap); 255 | graphics.drawBitmap(0, 0, bitmap); 256 | graphics.end(); 257 | 258 | // console.writeln("Preview drawn."); 259 | } 260 | 261 | // Track mouse position 262 | var lastMousePos = null; 263 | 264 | // Setup mouse event handlers for drawing 265 | function installPolygonHandler() { 266 | if (!targetWindow) return; 267 | 268 | // Mouse press handler - add points to the active polygon 269 | previewControl.onMousePress = function(x, y, button, buttons, modifiers) { 270 | // console.writeln("Mouse Press: " + x + ", " + y); 271 | if (!isDrawing) return false; 272 | // if (button != MouseButton_Left) return false; 273 | 274 | if (0) { 275 | // Double click closes the polygon 276 | var now = Date.now(); 277 | if (targetWindow.lastClickTime && now - targetWindow.lastClickTime < 300 && activePolygon.length > 2) { 278 | finishPolygon(); 279 | return true; 280 | } 281 | targetWindow.lastClickTime = now; 282 | } 283 | // Check if the point is already in the polygon 284 | for (var i = 0; i < activePolygon.length; i++) { 285 | if (activePolygon[i].x == x && activePolygon[i].y == y) { 286 | // console.writeln("Point already in polygon: " + x + ", " + y); 287 | return false; // Ignore duplicate points 288 | } 289 | } 290 | // Add point to active polygon 291 | activePolygon.push({ x: x, y: y }); 292 | updatePreview(); 293 | 294 | return true; 295 | }; 296 | 297 | // Mouse move handler - track mouse position for live preview 298 | previewControl.onMouseMove = function(x, y, buttons, modifiers) { 299 | if (!isDrawing) return false; 300 | 301 | // console.writeln("Mouse Pos: " + x + ", " + y); 302 | // lastMousePos = { x: x, y: y }; 303 | // updatePreview(); 304 | 305 | return false; // Don't consume the event 306 | }; 307 | } 308 | 309 | // Remove event handlers 310 | function uninstallPolygonHandler() { 311 | if (!targetWindow) return; 312 | 313 | previewControl.onMousePress = null; 314 | previewControl.onMouseMove = null; 315 | } 316 | 317 | // Complete the current polygon and add it to exclusion areas 318 | function finishPolygon() { 319 | if (activePolygon.length > 2) { 320 | // Close the polygon by connecting the last point to the first 321 | activePolygon.push(activePolygon[0]); 322 | // console.writeln("Closing polygon: " + activePolygon[0].x + ", " + activePolygon[0].y); 323 | // Add a copy of the active polygon to our exclusion areas 324 | exclusionAreas.push(activePolygon.slice()); 325 | updateExclusionCount(); 326 | } 327 | 328 | // Reset for next polygon 329 | activePolygon = []; 330 | isDrawing = false; 331 | uninstallPolygonHandler(); 332 | updatePreview(); 333 | 334 | dialog.startDrawing_Button.enabled = true; 335 | dialog.finishDrawing_Button.enabled = false; 336 | dialog.cancelDrawing_Button.enabled = false; 337 | } 338 | 339 | // Update the exclusion count label 340 | function updateExclusionCount() { 341 | dialog.exclusionCount_Label.text = "Exclusion areas: " + exclusionAreas.length; 342 | } 343 | 344 | // Force a preview update 345 | function updatePreview() { 346 | if (previewControl) { 347 | previewControl.update(); 348 | } 349 | } 350 | 351 | function getScale(targetView) { 352 | var imgWidth = targetView.image.width; 353 | var imgHeight = targetView.image.height; 354 | 355 | // Set reasonable limits for the dialog size 356 | var maxPreviewWidth = Math.min(800, imgWidth); 357 | var maxPreviewHeight = Math.min(600, imgHeight); 358 | 359 | // Calculate scaling to fit within our max dimensions 360 | scale = Math.min(maxPreviewWidth / imgWidth, maxPreviewHeight / imgHeight); 361 | } 362 | 363 | // Set up preview for selected view 364 | function setPreviewForView() { 365 | // Update the preview size to match image aspect ratio if the view changes 366 | if (targetView && previewControl) { 367 | var imgWidth = targetView.image.width; 368 | var imgHeight = targetView.image.height; 369 | 370 | // Set the preview size 371 | previewControl.setFixedSize(Math.round(imgWidth * scale), Math.round(imgHeight * scale)); 372 | 373 | // Force dialog to adjust to the new control size 374 | dialog.adjustToContents(); 375 | } 376 | 377 | updatePreview(); 378 | } 379 | 380 | // Export mask image to be used with other processes 381 | function exportExclusionMask(targetWindow, exclusionAreas) { 382 | targetView = targetWindow.mainView; 383 | 384 | if (exclusionAreas.length == 0) { 385 | // No exclusion areas defined 386 | return null; 387 | } 388 | 389 | var width = targetView.image.width; 390 | var height = targetView.image.height; 391 | 392 | // Create a new image 393 | var maskWindow = new ImageWindow(width, height, 1, 32, true, false, "ExclusionMask"); 394 | var maskView = maskWindow.mainView; 395 | 396 | // Initialize to black (0) 397 | maskView.image.fill(0); 398 | 399 | // Draw exclusion polygons as white (1) 400 | // We could use the VectorGraphics class here, but for simplicity, 401 | // we'll just use the isPointInPolygon function to set each pixel 402 | maskView.beginProcess(UndoFlag_NoSwapFile); 403 | 404 | for (var y = 0; y < height; y++) { 405 | for (var x = 0; x < width; x++) { 406 | if (isPointExcluded(x, y)) { 407 | maskView.image.setSample(x, y, 0, 1); // Set to white (1) 408 | } 409 | } 410 | } 411 | 412 | maskView.endProcess(); 413 | 414 | return maskWindow; 415 | } 416 | 417 | // Get exclusion areas scaled to the target image size 418 | function getScaledExclusionAreas() { 419 | var scaleX = targetView.image.width / previewControl.width; 420 | var scaleY = targetView.image.height / previewControl.height; 421 | 422 | var scaledExclusionAreas = []; 423 | for (var i = 0; i < exclusionAreas.length; i++) { 424 | var polygon = exclusionAreas[i]; 425 | var scaledPolygon = []; 426 | for (var j = 0; j < polygon.length; j++) { 427 | // console.writeln("Scaling point: " + polygon[j].x + ", " + polygon[j].y); 428 | scaledPolygon.push({ x: Math.floor(polygon[j].x * scaleX), y: Math.floor(polygon[j].y * scaleY) }); 429 | } 430 | scaledExclusionAreas.push(scaledPolygon); 431 | } 432 | 433 | return scaledExclusionAreas; 434 | } 435 | 436 | function scaleExclusionAreasToImage(exclusionAreas, targetView) { 437 | var scaledExclusionAreas = []; 438 | for (var i = 0; i < exclusionAreas.length; i++) { 439 | var polygon = exclusionAreas[i]; 440 | var scaledPolygon = []; 441 | for (var j = 0; j < polygon.length; j++) { 442 | // console.writeln("Scaling point: " + polygon[j].x + ", " + polygon[j].y); 443 | scaledPolygon.push({ x: Math.floor(polygon[j].x * scale), y: Math.floor(polygon[j].y * scale) }); 444 | } 445 | scaledExclusionAreas.push(scaledPolygon); 446 | } 447 | 448 | return scaledExclusionAreas; 449 | } 450 | 451 | // Main script entry point 452 | function main(activeWindow, currentExclusionAreas) { 453 | 454 | targetWindow = activeWindow; 455 | targetView = targetWindow.currentView; 456 | 457 | // Get the image scale 458 | getScale(targetView); 459 | 460 | exclusionAreas = scaleExclusionAreasToImage(currentExclusionAreas, targetView); 461 | 462 | // Create and execute dialog 463 | dialog = new ExclusionAreaDialog(); 464 | return dialog.execute(); 465 | } 466 | 467 | this.main = main; 468 | this.exportExclusionMask = exportExclusionMask; 469 | this.getScaledExclusionAreas = getScaledExclusionAreas; 470 | 471 | } /* AutoIntegrateExclusionArea */ 472 | 473 | AutoIntegrateExclusionArea.prototype = new Object; 474 | -------------------------------------------------------------------------------- /AutoIntegrateGlobal.js: -------------------------------------------------------------------------------- 1 | /* 2 | AutoIntegrate Global variables. 3 | 4 | Copyright (c) 2018-2025 Jarmo Ruuth. 5 | 6 | Crop to common area code 7 | 8 | Copyright (c) 2022 Jean-Marc Lugrin. 9 | 10 | Window name prefix and icon location code 11 | 12 | Copyright (c) 2021 rob pfile. 13 | 14 | This product is based on software from the PixInsight project, developed 15 | by Pleiades Astrophoto and its contributors (https://pixinsight.com/). 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define SETTINGSKEY "AutoIntegrate" 24 | 25 | /* 26 | * Default STF Parameters 27 | */ 28 | 29 | // Shadows clipping point in (normalized) MAD units from the median. 30 | #define DEFAULT_AUTOSTRETCH_SCLIP -2.80 31 | // Target mean background in the [0,1] range. 32 | #define DEFAULT_AUTOSTRETCH_TBGND 0.25 33 | // Apply the same STF to all nominal channels (true), or treat each channel 34 | // separately (false). 35 | #define DEFAULT_AUTOSTRETCH_CLINK true 36 | 37 | function AutoIntegrateGlobal() 38 | { 39 | 40 | this.__base__ = Object; 41 | this.__base__(); 42 | 43 | /* Following variables are AUTOMATICALLY PROCESSED so do not change format. 44 | */ 45 | this.autointegrate_version = "AutoIntegrate v1.76 test8"; // Version, also updated into updates.xri 46 | this.autointegrate_info = "Exclusion areas, metrics visualizer"; // For updates.xri 47 | 48 | this.autointegrate_version_info = [ 49 | "Changes since the previous version:", 50 | "- Exclusion areas can now be defined in the GUI for DBE.", 51 | "- Metrics visualizer can now be used to visualize metrics from SubframeSelector.", 52 | "- Astrobin compatible .csv file is generated after a full processing.", 53 | "- Create RGB stars for narrowband when RGB channels are present." 54 | ]; 55 | 56 | this.pixinsight_version_str = ""; // PixInsight version string, e.g. 1.8.8.10 57 | this.pixinsight_version_num = 0; // PixInsight version number, e.h. 1080810 58 | this.pixinsight_build_num = 0; // PixInsight build number, e.g. 1601 59 | 60 | this.processingDate = null; 61 | 62 | this.processing_state = { 63 | none: 0, 64 | processing: 1, 65 | extra_processing: 2 66 | } 67 | 68 | // GUI variables 69 | this.tabStatusInfoLabel = null; // For update processing status 70 | this.sideStatusInfoLabel = null; // For update processing status 71 | 72 | this.do_not_read_settings = false; // do not read Settings from persistent module settings 73 | this.do_not_write_settings = false; // do not write Settings to persistent module settings 74 | this.use_preview = true; 75 | this.is_processing = this.processing_state.none; 76 | 77 | this.cancel_processing = false; 78 | 79 | this.LDDDefectInfo = []; // { groupname: name, defects: defects } 80 | 81 | /* 82 | Parameters that can be adjusted in the GUI 83 | These can be saved to persistent module settings or 84 | process icon and later restored. 85 | Note that there is another parameter set ppar which are 86 | saved only to persistent module settings. 87 | For reset, we need to keep track of GUI element where 88 | these values are used. Fields where values are stored 89 | are: .currentItem, .checked, .editText, .setValue, .value 90 | */ 91 | this.par = { 92 | // Image processing parameters 93 | local_normalization: { val: false, def: false, name : "Local normalization", type : 'B' }, 94 | fix_column_defects: { val: false, def: false, name : "Fix column defects", type : 'B' }, 95 | fix_row_defects: { val: false, def: false, name : "Fix row defects", type : 'B' }, 96 | skip_cosmeticcorrection: { val: false, def: false, name : "No Cosmetic correction", type : 'B', oldname: "Cosmetic correction" }, 97 | skip_subframeselector: { val: false, def: false, name : "No SubframeSelector", type : 'B', oldname : "SubframeSelector" }, 98 | strict_StarAlign: { val: false, def: false, name : "Strict StarAlign", type : 'B' }, 99 | staralignment_sensitivity: { val: 0.5, def: 0.5, name : "StarAlignment sensitivity", type : 'R' }, 100 | staralignment_maxstarsdistortion: { val: 0.6, def: 0.6, name : "StarAlignment distortion", type : 'R' }, 101 | staralignment_structurelayers: { val: 5, def: 5, name : "StarAlignment layers", type : 'I' }, 102 | staralignment_noisereductionfilterradius: { val: 0, def: 0, name : "StarAlignment noise reduction", type : 'I' }, 103 | comet_align: { val: false, def: false, name : "Comet align", type : 'B' }, 104 | comet_first_xy: { val: '', def: '', name : "Comet align first XY", type : 'S' }, 105 | comet_last_xy: { val: '', def: '', name : "Comet align last XY", type : 'S' }, 106 | binning: { val: 0, def: 0, name : "Binning", type : 'I' }, 107 | binning_resample: { val: 2, def: 2, name : "Binning resample factor", type : 'I' }, 108 | GC_before_channel_combination: { val: false, def: false, name : "GC before channel combination", type : 'B', oldname: "ABE before channel combination" }, 109 | GC_on_lights: { val: false, def: false, name : "GC on light images", type : 'B', oldname: "ABE on light images" }, 110 | use_GC_on_L_RGB: { val: false, def: false, name : "Use GC on L, RGB", type : 'B', oldname: "Use ABE on L, RGB" }, 111 | use_GC_on_L_RGB_stretched: { val: false, def: false, name : "Use GC on L, RGB stretched", type : 'B', oldname: "Use ABE on L, RGB stretched" }, 112 | use_graxpert: { val: false, def: false, name : "Use GraXpert", type : 'B' }, 113 | use_graxpert_denoise: { val: false, def: false, name : "Use GraXpert denoise", type : 'B' }, 114 | use_graxpert_deconvolution: { val: false, def: false, name : "Use GraXpert deconvolution", type : 'B' }, 115 | use_abe: { val: false, def: false, name : "Use AutomaticBackgroundExtractor", type : 'B' }, 116 | use_dbe: { val: false, def: false, name : "Use DynamicBackgroundExtractor", type : 'B' }, 117 | use_multiscalegradientcorrection: { val: false, def: false, name : "Use MultiscaleGradientCorrection", type : 'B' }, 118 | skip_color_calibration: { val: false, def: false, name : "No color calibration", type : 'B' }, 119 | skip_auto_background: { val: false, def: false, name : "No auto background", type : 'B' }, 120 | use_spcc: { val: false, def: false, name : "Use SPCC for color calibration", type : 'B' }, 121 | solve_image: { val: false, def: false, name : "Solve image", type : 'B' }, 122 | use_background_neutralization: { val: false, def: false, name : "Background neutralization", type : 'B' }, 123 | use_fastintegration: { val: false, def: false, name : "Use FastIntegration", type : 'B' }, 124 | use_imageintegration_ssweight: { val: false, def: false, name : "ImageIntegration use SSWEIGHT", type : 'B' }, 125 | skip_noise_reduction: { val: false, def: false, name : "No noise reduction", type : 'B' }, 126 | skip_star_noise_reduction: { val: false, def: false, name : "No star noise reduction", type : 'B' }, 127 | skip_mask_contrast: { val: false, def: false, name : "No mask contrast", type : 'B' }, 128 | skip_sharpening: { val: false, def: false, name : "No sharpening", type : 'B' }, 129 | skip_SCNR: { val: false, def: false, name : "No SCNR", type : 'B' }, 130 | force_new_mask: { val: false, def: false, name : "Force new mask", type : 'B' }, 131 | crop_to_common_area: { val: false, def: false, name : "Crop to common area", type : 'B' }, 132 | unscreen_stars: { val: true, def: true, name : "Unscreen stars", type : 'B' }, 133 | 134 | // Saving image 135 | save_final_image_tiff: { val: false, def: false, name : "Save final image as TIFF", type : 'B' }, 136 | save_final_image_jpg: { val: false, def: false, name : "Save final image as JPG", type : 'B' }, 137 | save_final_image_jpg_quality: { val: 80, def: 80, name : "Save final image as JPG quality", type : 'I' }, 138 | 139 | // Other parameters 140 | calibrate_only: { val: false, def: false, name : "Calibrate only", type : 'B' }, 141 | image_weight_testing: { val: false, def: false, name : "Image weight testing", type : 'B' }, 142 | early_PSF_check: { val: false, def: false, name : "Early PSF check", type : 'B' }, 143 | debayer_only: { val: false, def: false, name : "Debayer only", type : 'B' }, 144 | binning_only: { val: false, def: false, name : "Binning only", type : 'B' }, 145 | extract_channels_only: { val: false, def: false, name : "Extract channels only", type : 'B' }, 146 | integrate_only: { val: false, def: false, name : "Integrate only", type : 'B' }, 147 | channelcombination_only: { val: false, def: false, name : "ChannelCombination only", type : 'B' }, 148 | cropinfo_only: { val: false, def: false, name : "Crop info only", type : 'B' }, 149 | RRGB_image: { val: false, def: false, name : "RRGB", type : 'B' }, 150 | batch_mode: { val: false, def: false, name : "Batch mode", type : 'B' }, 151 | fast_mode: { val: false, def: false, name : "Fast mode", type : 'B' }, 152 | fast_mode_opt: { val: 'S', def: 'S', name : "Fast mode opt", type : 'S' }, 153 | substack_mode: { val: false, def: false, name : "Substack mode", type : 'B' }, 154 | substack_count: { val: 10, def: 10, name : "Substack count", type : 'I' }, 155 | skip_autodetect_filter: { val: false, def: false, name : "Do not autodetect FILTER keyword", type : 'B' }, 156 | skip_autodetect_imagetyp: { val: false, def: false, name : "Do not autodetect IMAGETYP keyword", type : 'B' }, 157 | select_all_files: { val: false, def: false, name : "Select all files", type : 'B' }, 158 | save_all_files: { val: false, def: false, name : "Save all files", type : 'B' }, 159 | save_processed_channel_images: { val: false, def: false, name : "Save processed channel images", type : 'B' }, 160 | save_stretched_starless_channel_images: { val: false, def: false, name: "Save starless channel images", type: 'B' }, 161 | stretched_channel_auto_contrast: { val: false, def: false, name : "Stretched channel auto contrast", type : 'B' }, 162 | no_subdirs: { val: false, def: false, name : "No subdirectories", type : 'B' }, 163 | use_drizzle: { val: false, def: false, name : "Drizzle", type : 'B' }, 164 | drizzle_scale: { val: 2, def: 2, name : "Drizzle scale", type : 'I' }, 165 | drizzle_drop_shrink: { val: 0.9, def: 0.9, name : "Drizzle drop shrink", type : 'R' }, 166 | keep_integrated_images: { val: false, def: false, name : "Keep integrated images", type : 'B' }, 167 | reset_on_setup_load: { val: true, def: true, name : "Reset on setup load", type : 'B' }, 168 | keep_temporary_images: { val: false, def: false, name : "Keep temporary images", type : 'B' }, 169 | keep_processed_images: { val: false, def: false, name : "Keep processed images", type : 'B' }, 170 | debug: { val: false, def: false, name : "Debug", type : 'B' }, 171 | flowchart_debug: { val: false, def: false, name : "Flowchart debug", type : 'B' }, 172 | print_process_values: { val: false, def: false, name : "Print process values", type : 'B' }, 173 | monochrome_image: { val: false, def: false, name : "Monochrome", type : 'B' }, 174 | skip_imageintegration_clipping: { val: false, def: false, name : "No ImageIntegration clipping", type : 'B' }, 175 | synthetic_l_image: { val: false, def: false, name : "Synthetic L", type : 'B' }, 176 | synthetic_missing_images: { val: false, def: false, name : "Synthetic missing image", type : 'B' }, 177 | force_file_name_filter: { val: false, def: false, name : "Use file name for filters", type : 'B' }, 178 | unique_file_names: { val: false, def: false, name : "Unique file names", type : 'B' }, 179 | use_starxterminator: { val: false, def: false, name : "Use StarXTerminator", type : 'B' }, 180 | run_get_flowchart_data: { val: false, def: false, name : "Run get flowchart data", type : 'B', skip_reset: true }, 181 | flowchart_background_image: { val: true, def: true, name : "Flowchart background image", type : 'B', skip_reset: true }, 182 | flowchart_time: { val: true, def: true, name : "Flowchart time", type : 'B', skip_reset: true }, 183 | flowchart_saveimage: { val: false, def: false, name : "Flowchart save image", type : 'B', skip_reset: true }, 184 | 185 | use_blurxterminator: { val: false, def: false, name : "Use BlurXTerminator", type : 'B' }, 186 | bxt_sharpen_stars: { val: 0.25, def: 0.25, name : "BlurXTerminator sharpen stars", type : 'R' }, 187 | bxt_adjust_halo: { val: 0.00, def: 0.00, name : "BlurXTerminator adjust halos", type : 'R' }, 188 | bxt_sharpen_nonstellar: { val: 0.90, def: 0.90, name : "BlurXTerminator sharpen nonstellar", type : 'R' }, 189 | bxt_psf: { val: 0, def: 0, name : "BlurXTerminator PSF", type : 'R' }, 190 | bxt_image_psf: { val: false, def: false, name : "BlurXTerminator image PSF", type : 'B' }, 191 | bxt_median_psf: { val: false, def: false, name : "BlurXTerminator median PSF", type : 'B' }, 192 | bxt_correct_only_before_cc: { val: false, def: false, name : "BlurXTerminator correct only before CC", type : 'B' }, 193 | bxt_correct_channels: { val: false, def: false, name : "BlurXTerminator correct only channels", type : 'B' }, 194 | nxt_denoise: { val: 0.9, def: 0.9, name : "NoiseXTerminator denoise", type : 'R' }, 195 | nxt_iterations: { val: 2, def: 2, name : "NoiseXTerminator iterations", type : 'I' }, 196 | nxt_enable_color_separation: { val: false, def: false, name : "NoiseXTerminator enable color separation", type : 'B' }, 197 | nxt_denoise_color: { val: 0.9, def: 0.9, name : "NoiseXTerminator denoise color", type : 'R' }, 198 | nxt_enable_frequency_separation: { val: false, def: false, name : "NoiseXTerminator enable frequency separation", type : 'B' }, 199 | nxt_denoise_lf: { val: 0.9, def: 0.9, name : "NoiseXTerminator denoise LF", type : 'R' }, 200 | nxt_frequency_scale: { val: 5, def: 5, name : "NoiseXTerminator frequency scale", type : 'R' }, 201 | nxt_denoise_lf_color: { val: 0.9, def: 0.9, name : "NoiseXTerminator denoise LF color", type : 'R' }, 202 | nxt_detail: { val: 0.15, def: 0.15, name : "NoiseXTerminator detail", type : 'R' }, // Old 203 | 204 | deepsnr_amount: { val: 0.8, def: 0.8, name : "DeepSNR amount", type : 'R' }, 205 | 206 | use_noisexterminator: { val: false, def: false, name : "Use NoiseXTerminator", type : 'B' }, 207 | use_starnet2: { val: false, def: false, name : "Use StarNet2", type : 'B' }, 208 | use_deepsnr: { val: false, def: false, name : "Use DeepSNR", type : 'B' }, 209 | win_prefix_to_log_files: { val: false, def: false, name : "Add window prefix to log files", type : 'B' }, 210 | start_from_imageintegration: { val: false, def: false, name : "Start from ImageIntegration", type : 'B' }, 211 | generate_xdrz: { val: false, def: false, name : "Generate .xdrz files", type : 'B' }, 212 | autosave_setup: { val: true, def: true, name: "Autosave setup", type: 'B' }, 213 | use_processed_files: { val: false, def: false, name: "Use processed files", type: 'B' }, 214 | save_cropped_images: { val: false, def: false, name: "Save cropped images", type: 'B' }, 215 | 216 | open_directory: { val: false, def: false, name: "Open directory", type: 'B' }, 217 | directory_files: { val: "*.fits *.fit", def: "*.fits *.fit", name : "Directory files", type : 'S' }, 218 | 219 | // astrobin variables 220 | astrobin_L: { val: "", def: "", name : "AstroBin L", type : 'S', skip_reset: true }, 221 | astrobin_R: { val: "", def: "", name : "AstroBin R", type : 'S', skip_reset: true }, 222 | astrobin_G: { val: "", def: "", name : "AstroBin G", type : 'S', skip_reset: true }, 223 | astrobin_B: { val: "", def: "", name : "AstroBin B", type : 'S', skip_reset: true }, 224 | astrobin_H: { val: "", def: "", name : "AstroBin H", type : 'S', skip_reset: true }, 225 | astrobin_S: { val: "", def: "", name : "AstroBin S", type : 'S', skip_reset: true }, 226 | astrobin_O: { val: "", def: "", name : "AstroBin O", type : 'S', skip_reset: true }, 227 | astrobin_C: { val: "", def: "", name : "AstroBin C", type : 'S', skip_reset: true }, 228 | 229 | // Narrowband processing 230 | narrowband_mapping: { val: 'Auto', def: 'Auto', name : "Narrowband mapping", type : 'S' }, 231 | custom_R_mapping: { val: 'Auto', def: 'Auto', name : "Narrowband R mapping", type : 'S' }, 232 | custom_G_mapping: { val: 'Auto', def: 'Auto', name : "Narrowband G mapping", type : 'S' }, 233 | custom_B_mapping: { val: 'Auto', def: 'Auto', name : "Narrowband B mapping", type : 'S' }, 234 | custom_L_mapping: { val: 'Auto', def: 'Auto', name : "Narrowband L mapping", type : 'S' }, 235 | narrowband_linear_fit: { val: 'Auto', def: 'Auto', name : "Narrowband linear fit", type : 'S' }, 236 | mapping_on_nonlinear_data: { val: false, def: false, name : "Narrowband mapping on non-linear data", type : 'B' }, 237 | force_narrowband_mapping: { val: false, def: false, name : "Force narrowband mapping", type : 'B' }, 238 | remove_stars_before_stretch: { val: false, def: false, name : "Remove stars early", type : 'B' }, 239 | remove_stars_light: { val: false, def: false, name : "Remove stars light", type : 'B' }, 240 | remove_stars_channel: { val: false, def: false, name : "Remove stars channel", type : 'B' }, 241 | remove_stars_stretched: { val: false, def: false, name : "Remove stars stretched", type : 'B' }, 242 | create_RGB_stars: { val: false, def: false, name : "RGB stars", type : 'B' }, 243 | use_narrowband_multiple_mappings: { val: false, def: false, name : "Use narrowband multiple mappings", type : 'B' }, 244 | narrowband_multiple_mappings_list: { val: "", def: "", name : "Narrowband multiple mappings list", type : 'S' }, 245 | 246 | // Ha to RGB mapping 247 | use_RGBHa_Mapping: { val: false, def: false, name : "Ha RGB mapping", type : 'B' }, 248 | RGBHa_preset: { val: 'Combine Continuum Subtract', def: 'Combine Continuum Subtract', name : "Ha RGB mapping preset", type : 'S' }, 249 | RGBHa_prepare_method: { val: 'Continuum Subtract', def: 'Continuum Subtract', name : "Ha RGB mapping prepare method", type : 'S' }, 250 | RGBHa_combine_time: { val: 'Stretched', def: 'Stretched', name : "Ha RGB mapping combine time", type : 'S' }, 251 | RGBHa_combine_method: { val: 'Bright structure add', def: 'Bright structure add', name : "Ha RGB mapping combine method", type : 'S' }, 252 | RGBHa_noise_reduction: { val: true, def: true, name : "Ha RGB mapping noise reduction", type : 'B' }, 253 | RGBHa_boost: { val: 1.0, def: 1.0, name : "Ha RGB boost", type : 'R' }, 254 | RGBHa_gradient_correction: { val: false, def: false, name : "Ha RGB mapping gradient correction", type : 'B' }, 255 | RGBHa_smoothen_background: { val: false, def: false, name : "Ha RGB mapping smoothen background", type : 'B' }, 256 | RGBHa_smoothen_background_value: { val: 25, def: 25, name : "Ha RGB mapping smoothen background value", type : 'R' }, 257 | RGBHa_remove_stars: { val: false, def: false, name : "Ha RGB mapping remove stars", type : 'B' }, 258 | RGBHa_Combine_BoostFactor: { val: 1.0, def: 1.0, name : "Ha RGB mapping combine boost factor", type : 'R' }, 259 | RGBHa_Add_BoostFactor: { val: 0.5, def: 0.5, name : "Ha RGB mapping SPCC boost factor", type : 'R' }, 260 | RGBHa_test_value: { val: 'Mapping', def: 'Mapping', name : "Narrowband RGB mapping O bandwidth", type : 'R' }, 261 | 262 | // Narrowband to RGB mapping 263 | use_RGBNB_Mapping: { val: false, def: false, name : "Narrowband RGB mapping", type : 'B' }, 264 | RGBNB_use_RGB_image: { val: false, def: false, name : "Narrowband RGB mapping use RGB", type : 'B' }, 265 | RGBNB_gradient_correction: { val: false, def: false, name : "Narrowband RGB mapping gradient correction", type : 'B' }, 266 | RGBNB_linear_fit: { val: false, def: false, name : "Narrowband RGB mapping linear fit", type : 'B' }, 267 | RGBNB_L_mapping: { val: '', def: '', name : "Narrowband RGB mapping for L", type : 'S' }, 268 | RGBNB_R_mapping: { val: 'H', def: 'H', name : "Narrowband RGB mapping for R", type : 'S' }, 269 | RGBNB_G_mapping: { val: '', def: '', name : "Narrowband RGB mapping for G", type : 'S' }, 270 | RGBNB_B_mapping: { val: '', def: '', name : "Narrowband RGB mapping for B", type : 'S' }, 271 | RGBNB_L_BoostFactor: { val: 1.2, def: 1.2, name : "Narrowband RGB mapping L boost factor", type : 'R' }, 272 | RGBNB_R_BoostFactor: { val: 1.2, def: 1.2, name : "Narrowband RGB mapping R boost factor", type : 'R' }, 273 | RGBNB_G_BoostFactor: { val: 1.2, def: 1.2, name : "Narrowband RGB mapping G boost factor", type : 'R' }, 274 | RGBNB_B_BoostFactor: { val: 1.2, def: 1.2, name : "Narrowband RGB mapping B boost factor", type : 'R' }, 275 | RGBNB_L_bandwidth: { val: 300, def: 300, name : "Narrowband RGB mapping L bandwidth", type : 'R' }, 276 | RGBNB_R_bandwidth: { val: 100, def: 100, name : "Narrowband RGB mapping R bandwidth", type : 'R' }, 277 | RGBNB_G_bandwidth: { val: 100, def: 100, name : "Narrowband RGB mapping G bandwidth", type : 'R' }, 278 | RGBNB_B_bandwidth: { val: 100, def: 100, name : "Narrowband RGB mapping B bandwidth", type : 'R' }, 279 | RGBNB_H_bandwidth: { val: 3, def: 3, name : "Narrowband RGB mapping H bandwidth", type : 'R' }, 280 | RGBNB_S_bandwidth: { val: 3, def: 3, name : "Narrowband RGB mapping S bandwidth", type : 'R' }, 281 | RGBNB_O_bandwidth: { val: 3, def: 3, name : "Narrowband RGB mapping O bandwidth", type : 'R' }, 282 | 283 | // Processing settings 284 | auto_noise_reduction: { val: true, def: true, name : "Auto noise reduction", type : 'B' }, 285 | channel_noise_reduction: { val: false, def: false, name : "Channel noise reduction", type : 'B' }, 286 | non_linear_noise_reduction: { val: false, def: false, name : "Non-linear noise reduction", type : 'B' }, 287 | noise_reduction_strength: { val: 3, def: 3, name : "Noise reduction strength", type : 'I' }, 288 | luminance_noise_reduction_strength: { val: 3, def: 3, name : "Noise reduction strength on luminance image", type : 'I' }, 289 | combined_image_noise_reduction: { val: false, def: false, name : "Do noise reduction on integrated image", type : 'B' }, 290 | processed_image_noise_reduction: { val: false, def: false, name : "Do noise reduction on processed image", type : 'B', oldname : "Do noise reduction on combined image" }, 291 | use_color_noise_reduction: { val: false, def: false, name : "Color noise reduction", type : 'B' }, 292 | use_ACDNR_noise_reduction: { val: true, def: true, name : "Use ACDNR noise reduction", type : 'B' }, 293 | ACDNR_noise_reduction: { val: 1.0, def: 1.0, name : "ACDNR noise reduction", type : 'R' }, 294 | use_weight: { val: 'PSF Signal', def: 'PSF Signal', name : "Weight calculation", type : 'S' }, 295 | ssweight_limit: { val: 0.0000000001, def: 0.0000000001, name : "SSWEIGHT limit", type : 'R' }, 296 | sort_order_type: { val: 'SSWEIGHT', def: 'SSWEIGHT', name : "Filtering sort order", type : 'S' }, 297 | filter_limit1_type: { val: 'PSFSignal', def: 'PSFSignal', name : "Filter limit 1 type", type : 'S' }, 298 | filter_limit1_val: { val: 0, def: 0, name : "Filter limit 1 value", type : 'R' }, 299 | filter_limit2_type: { val: 'FWHM', def: 'FWHM', name : "Filter limit 2 type", type : 'S' }, 300 | filter_limit2_val: { val: 0, def: 0, name : "Filter limit 2 value", type : 'R' }, 301 | filter_limit3_type: { val: 'Eccentricity', def: 'Eccentricity', name : "Filter limit 3 type", type : 'S' }, 302 | filter_limit3_val: { val: 0, def: 0, name : "Filter limit 3 value", type : 'R' }, 303 | filter_limit4_type: { val: 'Stars', def: 'Stars', name : "Filter limit 4 type", type : 'S' }, 304 | filter_limit4_val: { val: 0, def: 0, name : "Filter limit 4 value", type : 'R' }, 305 | outliers_ssweight: { val: false, def: false, name : "Outliers SSWEIGHT", type : 'B' }, 306 | outliers_fwhm: { val: false, def: false, name : "Outliers FWHM", type : 'B' }, 307 | outliers_ecc: { val: false, def: false, name : "Outliers eccentricity", type : 'B' }, 308 | outliers_snr: { val: false, def: false, name : "Outliers SNR", type : 'B' }, 309 | outliers_psfsignal: { val: false, def: false, name : "Outliers PSF Signal", type : 'B' }, 310 | outliers_psfpower: { val: false, def: false, name : "Outliers PSF Power", type : 'B' }, 311 | outliers_stars: { val: false, def: false, name : "Outliers Stars", type : 'B' }, 312 | outliers_method: { val: 'Two sigma', def: 'Two sigma', name : "Outlier method", type : 'S' }, 313 | outliers_minmax: { val: false, def: false, name : "Outlier min max", type : 'B' }, 314 | use_linear_fit: { val: 'Auto', def: 'Auto', name : "Linear fit", type : 'S' }, 315 | 316 | gc_scale: { val: 5, def: 5, name : "GC scale", type : 'R' }, 317 | gc_smoothness: { val: 0.4, def: 0.4, name : "GC smoothness", type : 'R' }, 318 | gc_automatic_convergence: { val: false, def: false, name : "GC automatic convergence", type : 'B' }, 319 | gc_structure_protection: { val: true, def: true, name : "GC structure protection", type : 'B' }, 320 | gc_protection_threshold: { val: 0.10, def: 0.10, name : "GC protection threshold", type : 'R' }, 321 | gc_protection_amount: { val: 0.50, def: 0.50, name : "GC protection amount", type : 'R' }, 322 | gc_output_background_model: { val: false, def: false, name : "GC output background model", type : 'B' }, 323 | gc_simplified_model: { val: false, def: false, name : "GC simplified model", type : 'B' }, 324 | gc_simplified_model_degree: { val: 1, def: 1, name : "GC simplified model degree", type : 'I' }, 325 | mgc_scale: { val: '1024', def: '1024', name : "MGC scale", type : 'S' }, 326 | mgc_output_background_model: { val: false, def: false, name : "MGC output background model", type : 'B' }, 327 | mgc_scale_factor: { val: 1.0, def: 1.0, name : "MGC scale factor", type : 'R' }, 328 | mgc_structure_separation: { val: 3, def: 3, name : "MGC structure separation", type : 'I' }, 329 | 330 | ABE_degree: { val: 4, def: 4, name : "ABE function degree", type : 'I' }, 331 | ABE_correction: { val: 'Subtraction', def: 'Subtraction', name : "ABE correction", type : 'S' }, 332 | ABE_normalize: { val: false, def: false, name : "ABE normalize", type : 'B' }, 333 | 334 | dbe_use_background_neutralization: { val: false, def: false, name : "DBE use background neutralization", type : 'B' }, 335 | dbe_use_abe: { val: false, def: false, name : "DBE use ABE", type : 'B' }, 336 | dbe_samples_per_row: { val: 10, def: 10, name : "DBE samples per row", type : 'I' }, 337 | dbe_normalize : { val: false, def: false, name : "DBE normalize", type : 'B' }, 338 | dbe_min_weight : { val: 0.75, def: 0.75, name : "DBE min weight", type : 'I' }, 339 | 340 | graxpert_path: { val: "", def: "", name : "GraXpert path", type : 'S', skip_reset: true }, 341 | graxpert_correction: { val: "Subtraction", def: "Subtraction", name : "GraXpert correction", type : 'S' }, 342 | graxpert_smoothing: { val: 0.5, def: 0.5, name : "GraXpert smoothing", type : 'R' }, 343 | graxpert_denoise_strength: { val: 0.5, def: 0.5, name : "GraXpert denoise strength", type : 'R' }, 344 | graxpert_denoise_batch_size: { val: '4', def: '4', name : "GraXpert denoise batch size", type : 'S' }, 345 | graxpert_deconvolution_stellar_strength: { val: 0.5, def: 0.5, name : "GraXpert deconvolution stellar strength", type : 'R' }, 346 | graxpert_deconvolution_stellar_psf: { val: 2.0, def: 2.0, name : "GraXpert deconvolution stellar PSF", type : 'R' }, 347 | graxpert_deconvolution_nonstellar_strength: { val: 0.5, def: 0.5, name : "GraXpert deconvolution nonstellar strength", type : 'R' }, 348 | graxpert_deconvolution_nonstellar_psf: { val: 2.0, def: 2.0, name : "GraXpert deconvolution nonstellar PSF", type : 'R' }, 349 | graxpert_median_psf: { val: true, def: true, name : "GraXpert median PSF", type : 'B' }, 350 | 351 | starxterminator_ai_model: { val: "", def: "", name : "StarXTerminator AI model", type : 'S', skip_reset: true }, 352 | starxterminator_large_overlap: { val: false, def: false, name : "StarXTerminator large overlap", type : 'B' }, 353 | crop_tolerance: { val: 2, def: 2, name : "Crop tolerance", type : 'I' }, 354 | crop_use_rejection_low: { val: true, def: true, name : "Crop use rejection low", type : 'B' }, 355 | crop_rejection_low_limit: { val: 0.2, def: 0.2, name : "Crop rejection low limit", type : 'R' }, 356 | crop_check_limit: { val: 5, def: 5, name : "Crop check limit", type : 'R' }, 357 | image_stretching: { val: 'Auto STF', def: 'Auto STF', name : "Image stretching", type : 'S' }, 358 | stars_stretching: { val: 'Arcsinh Stretch', def: 'Arcsinh Stretch', name : "Stars stretching", type : 'S' }, 359 | stars_combine: { val: 'Screen', def: 'Screen', name : "Stars combine", type : 'S' }, 360 | STF_linking: { val: 'Auto', def: 'Auto', name : "RGB channel linking", type : 'S' }, 361 | imageintegration_normalization: { val: 'Additive', def: 'Additive', name : "ImageIntegration Normalization", type : 'S' }, 362 | integration_combination: { val: 'Average', def: 'Average', name : "ImageIntegration Combination", type : 'S' }, 363 | use_clipping: { val: 'Auto2', def: 'Auto2', name : "ImageIntegration rejection", type : 'S' }, 364 | 365 | target_type: { val: 'Default', def: 'Default', name : "Target type", type : 'S' }, 366 | 367 | percentile_low: { val: 0.2, def: 0.2, name : "Percentile low", type : 'R' }, 368 | percentile_high: { val: 0.1, def: 0.1, name : "Percentile high", type : 'R' }, 369 | sigma_low: { val: 4.0, def: 4.0, name : "Sigma low", type : 'R' }, 370 | sigma_high: { val: 3.0, def: 3.0, name : "Sigma high", type : 'R' }, 371 | winsorised_cutoff: { val: 5.0, def: 5.0, name : "Winsorised cutoff", type : 'R' }, 372 | linearfit_low: { val: 5.0, def: 5.0, name : "Linear fit low", type : 'R' }, 373 | linearfit_high: { val: 4.0, def: 4.0, name : "Linear fit high", type : 'R' }, 374 | ESD_outliers: { val: 0.3, def: 0.3, name : "ESD outliers", type : 'R' }, 375 | ESD_significance: { val: 0.05, def: 0.05, name : "ESD significance", type : 'R' }, 376 | large_scale_pixel_rejection_high: { val: false, def: false, name : "Large scale pixel rejection high", type : 'B' }, 377 | large_scale_pixel_rejection_low: { val: false, def: false, name : "Large scale pixel rejection low", type : 'B' }, 378 | // ESD_lowrelaxation: { val: 1.50, def: 1.50, name : "ESD low relaxation", type : 'R' }, deprecated, use default for old version 379 | use_localnormalization_multiscale: { val: false, def: false, name : "Use LocalNormalization Multiscale", type : 'B' }, 380 | fastintegration_iterations: { val: 2, def: 2, name : "FastIntegration iterations", type : 'I' }, 381 | fastintegration_max_flux: { val: 0.5, def: 0.5, name : "FastIntegration max flux", type : 'R' }, 382 | fastintegration_errortolerance: { val: 1.5, def: 1.5, name : "FastIntegration error tolerance", type : 'R' }, 383 | fastintegration_fast_subframeselector: { val: true, def: true, name : "FastIntegration fast SubframeSelector", type : 'B' }, 384 | fastintegration_skip_cosmeticcorrection: { val: true, def: true, name : "FastIntegration skip CosmeticCorrection", type : 'B' }, 385 | drizzle_function: { val: 'Square', def: 'Square', name : "Drizzle function", type : 'S' }, 386 | drizzle_fast_mode: { val: true, def: true, name : "Drizzle fast mode", type : 'B' }, 387 | 388 | cosmetic_correction_hot_sigma: { val: 3, def: 3, name : "CosmeticCorrection hot sigma", type : 'I' }, 389 | cosmetic_correction_cold_sigma: { val: 3, def: 3, name : "CosmeticCorrection cold sigma", type : 'I' }, 390 | STF_targetBackground: { val: 0.25, def: 0.25, name : "STF targetBackground", type : 'R' }, 391 | MaskedStretch_targetBackground: { val: 0.125, def: 0.125, name : "Masked Stretch targetBackground", type : 'R' }, 392 | MaskedStretch_prestretch_target: { val: 0.1, def: 0.1, name : "Masked Stretch prestretch target", type : 'R' }, 393 | Arcsinh_stretch_factor: { val: 50, def: 50, name : "Arcsinh Stretch Factor", type : 'R' }, 394 | Arcsinh_black_point: { val: 0.01, def: 0.01, name : "Arcsinh Stretch black point", type : 'R' }, 395 | Arcsinh_iterations: { val: 3, def: 3, name : "Arcsinh Stretch iterations", type : 'I' }, 396 | LRGBCombination_lightness: { val: 0.5, def: 0.5, name : "LRGBCombination lightness", type : 'R' }, 397 | LRGBCombination_saturation: { val: 0.5, def: 0.5, name : "LRGBCombination saturation", type : 'R' }, 398 | LRGBCombination_linearfit: { val: false, def: false, name : "LRGBCombination linear fit", type : 'B' }, 399 | linear_increase_saturation: { val: 1, def: 1, name : "Linear saturation increase", type : 'I' }, 400 | non_linear_increase_saturation: { val: 1, def: 1, name : "Non-linear saturation increase", type : 'I' }, 401 | use_chrominance: { val: false, def: false, name : "Use chrominance", type : 'B' }, 402 | Hyperbolic_D: { val: 5, def: 5, name : "Hyperbolic Stretch D value", type : 'I' }, 403 | Hyperbolic_b: { val: 3, def: 3, name : "Hyperbolic Stretch b value", type : 'I' }, 404 | Hyperbolic_SP: { val: 10, def: 10, name : "Hyperbolic Stretch symmetry point value", type : 'I' }, 405 | Hyperbolic_target: { val: 0.25, def: 0.25, name : "Hyperbolic Stretch target", type : 'I' }, 406 | Hyperbolic_iterations: { val: 10, def: 10, name : "Hyperbolic Stretch iterations", type : 'I' }, 407 | Hyperbolic_mode: { val: 1, def: 1, name : "Hyperbolic Stretch mode", type : 'I' }, 408 | stretch_adjust_shadows: { val: "none", def: "none", name : "Stretch adjust shadows", type : 'S' }, 409 | stretch_adjust_shadows_perc: { val: 0.00, def: 0.00, name : "Stretch adjust shadows perc", type : 'R' }, 410 | histogram_stretch_type: { val: 'Median', def: 'Median', name : "Histogram stretch type", type : 'S' }, 411 | histogram_stretch_target: { val: 0.25, def: 0.25, name : "Histogram stretch target", type : 'I' }, 412 | other_stretch_target: { val: 0.25, def: 0.25, name : "Logarithmic stretch target", type : 'I' }, 413 | smoothbackground: { val: 0, def: 0, name : "Smooth background", type : 'R' }, 414 | target_name: { val: '', def: '', name : "Target name", type : 'S' }, 415 | target_radec: { val: '', def: '', name : "Target RA/DEC", type : 'S' }, 416 | target_focal: { val: '', def: '', name : "Target focal length", type : 'S' }, 417 | target_pixel_size: { val: '', def: '', name : "Target pixel size", type : 'S' }, 418 | target_binning: { val: 'Auto', def: 'Auto', name : "Target binning", type : 'S' }, 419 | target_drizzle: { val: 'Auto', def: 'Auto', name : "Target drizzle", type : 'S' }, 420 | target_forcesolve: { val: false, def: false, name : "Target force solve", type : 'B' }, 421 | target_interactivesolve: { val: false, def: false, name : "Target interactive solve", type : 'B' }, 422 | spcc_detection_scales: { val: 5, def: 5, name : "SPCC detection scales", type : 'I' }, 423 | spcc_noise_scales: { val: 1, def: 1, name : "SPCC noise scales", type : 'I' }, 424 | spcc_min_struct_size: { val: 0, def: 0, name : "SPCC min struct size", type : 'I' }, 425 | spcc_red_wavelength: { val: 671.60, def: 671.60, name : "SPCC red wavelength", type : 'R' }, 426 | spcc_red_bandwidth: { val: 3, def: 3, name : "SPCC red bandwidth", type : 'R' }, 427 | spcc_green_wavelength: { val: 656.30, def: 656.30, name : "SPCC green wavelength", type : 'R' }, 428 | spcc_green_bandwidth: { val: 3, def: 3, name : "SPCC green bandwidth", type : 'R' }, 429 | spcc_blue_wavelength: { val: 500.70, def: 500.70, name : "SPCC blue wavelength", type : 'R' }, 430 | spcc_blue_bandwidth: { val: 3, def: 3, name : "SSPCC blue bandwidth", type : 'R' }, 431 | spcc_narrowband_mode: { val: false, def: false, name : "SPCC narrowband mode", type : 'B' }, 432 | spcc_background_neutralization: { val: true, def: true, name : "SPCC background neutralization", type : 'B' }, 433 | spcc_auto_narrowband: { val: true, def: true, name : "SPCC narrowband auto mode", type : 'B' }, 434 | spcc_white_reference: { val: 'Average Spiral Galaxy', def: 'Average Spiral Galaxy', name : "SPCC white reference", type : 'S' }, 435 | spcc_limit_magnitude: { val: 'Auto', def: 'Auto', name : "SPCC limit magnitude", type : 'S' }, 436 | spcc_saturation_threshold: { val: 0.75, def: 0.75, name : "SPCC saturation threshold", type : 'R' }, 437 | spcc_min_SNR: { val: 40, def: 40, name : "SPCC min SNR", type : 'R' }, 438 | color_calibration_narrowband: { val: false, def: false, name : "ColorCalibration narrowband", type : 'B' }, 439 | color_calibration_time: { val: 'auto', def: 'auto', name : "ColorCalibration time", type : 'S' }, 440 | 441 | // Extra processing for narrowband 442 | run_foraxx_mapping: { val: false, def: false, name : "Extra Foraxx mapping", type : 'B' }, 443 | foraxx_palette: { val: "SHO", def: "SHO", name : "Extra Foraxx palette", type : 'S' }, 444 | run_extra_narrowband_mapping: { val: false, def: false, name : "Extra narrowband mapping", type : 'B' }, 445 | extra_narrowband_mapping_source_palette: { val: "SHO", def: "SHO", name : "Extra narrowband mapping source palette", type : 'S' }, 446 | extra_narrowband_mapping_target_palette: { val: "HOS", def: "HOS", name : "Extra narrowband mapping target palette", type : 'S' }, 447 | run_orangeblue_colors: { val: false, def: false, name : "Extra orangeblue colors", type : 'B' }, 448 | run_less_green_hue_shift: { val: false, def: false, name : "Extra narrowband green hue shift", type : 'B' }, 449 | run_orange_hue_shift: { val: false, def: false, name : "Extra narrowband more orange", type : 'B' }, 450 | run_hue_shift: { val: false, def: false, name : "Extra narrowband hue shift", type : 'B' }, 451 | 452 | run_colorized_narrowband: { val: false, def: false, name : "Extra colorized narrowband", type : 'B' }, 453 | colorized_integrated_images: { val: false, def: false, name : "Extra colorized narrowband integrated images", type : 'B' }, 454 | colorized_narrowband_preset: { val: "Default", def: "Default", name : "Extra colorized narrowband preset", type : 'S' }, 455 | narrowband_colorized_R_hue: { val: 0.0, def: 0.0, name : "Extra colorized narrowband R hue", type : 'R' }, 456 | narrowband_colorized_R_sat: { val: 0.5, def: 0.5, name : "Extra colorized narrowband R sat", type : 'R' }, 457 | narrowband_colorized_R_weight: { val: 1.0, def: 1.0, name : "Extra colorized narrowband R weight", type : 'R' }, 458 | narrowband_colorized_G_hue: { val: 0.33, def: 0.33, name : "Extra colorized narrowband G hue", type : 'R' }, 459 | narrowband_colorized_G_sat: { val: 0.5, def: 0.5, name : "Extra colorized narrowband G sat", type : 'R' }, 460 | narrowband_colorized_G_weight: { val: 1.0, def: 1.0, name : "Extra colorized narrowband G weight", type : 'R' }, 461 | narrowband_colorized_B_hue: { val: 0.67, def: 0.67, name : "Extra colorized narrowband B hue", type : 'R' }, 462 | narrowband_colorized_B_sat: { val: 0.5, def: 0.5, name : "Extra colorized narrowband B sat", type : 'R' }, 463 | narrowband_colorized_B_weight: { val: 1.0, def: 1.0, name : "Extra colorized narrowband B weight", type : 'R' }, 464 | narrowband_colorized_mapping: { val: 'RGB', def: 'RGB', name : "Extra colorized narrowband mapping", type : 'S' }, 465 | narrowband_colorized_combine: { val: 'Channels', def: 'Channels', name : "Extra colorized narrowband combine", type : 'S' }, 466 | narrowband_colorized_method: { val: 'PixelMath', def: 'PixelMath', name : "Extra colorized narrowband method", type : 'S' }, 467 | narrowband_colorized_linear_fit: { val: false, def: false, name : "Extra colorized narrowband linear fit", type : 'B' }, 468 | 469 | leave_some_green: { val: false, def: false, name : "Extra narrowband leave some green", type : 'B' }, 470 | leave_some_green_amount: { val: 0.50, def: 0.50, name : "Extra narrowband leave some green amount", type : 'R' }, 471 | run_narrowband_SCNR: { val: false, def: false, name : "Extra narrowband remove green", type : 'B' }, 472 | remove_magenta_color: { val: false, def: false, name : "Extra remove magenta color", type : 'B' }, 473 | fix_narrowband_star_color: { val: false, def: false, name : "Extra narrowband fix star colors", type : 'B' }, 474 | skip_star_fix_mask: { val: false, def: false, name : "Extra narrowband no star mask", type : 'B' }, 475 | 476 | // Generic Extra processing 477 | extra_remove_stars: { val: false, def: false, name : "Extra remove stars", type : 'B' }, 478 | extra_unscreen_stars: { val: false, def: false, name : "Extra unscreen stars", type : 'B' }, 479 | extra_fix_star_cores: { val: false, def: false, name : "Extra fix star cores", type : 'B' }, 480 | extra_combine_stars: { val: false, def: false, name : "Extra combine starless and stars", type : 'B' }, 481 | extra_combine_stars_mode: { val: 'Screen', def: 'Screen', name : "Extra remove stars combine", type : 'S' }, 482 | extra_combine_stars_image: { val: 'Auto', def: 'Auto', name : "Extra stars image", type : 'S' }, 483 | extra_combine_stars_reduce: { val: 'None', def: 'None', name : "Extra combine stars reduce", type : 'S' }, 484 | extra_combine_stars_reduce_S: { val: 0.15, def: 0.15, name : "Extra combine stars reduce S", type : 'R' }, 485 | extra_combine_stars_reduce_M: { val: 1, def: 1, name : "Extra combine stars reduce M", type : 'R' }, 486 | extra_backgroundneutralization: { val: false, def: false, name : "Extra background neutralization", type : 'B' }, 487 | extra_GC: { val: false, def: false, name : "Extra GC", type : 'B', oldname: 'Extra ABE' }, 488 | extra_GC_method: { val: 'Auto', def: 'Auto', name : "Extra GC method", type : 'S' }, 489 | extra_banding_reduction: { val: false, def: false, name : "Extra banding reduction", type : 'B' }, 490 | extra_darker_background: { val: false, def: false, name : "Extra Darker background", type : 'B' }, 491 | extra_darker_hightlights: { val: false, def: false, name : "Extra Darker highlights", type : 'B' }, 492 | extra_ET: { val: false, def: false, name : "Extra ExponentialTransformation", type : 'B' }, 493 | extra_ET_order: { val: 1.0, def: 1.0, name : "Extra ExponentialTransformation Order", type : 'I' }, 494 | extra_ET_adjusttype: { val: 'Lights', def: 'Lights', name : "Extra ExponentialTransformation adjust type", type : 'S' }, 495 | extra_HDRMLT: { val: false, def: false, name : "Extra HDRMLT", type : 'B' }, 496 | extra_HDRMLT_layers: { val: 6, def: 6, name : "Extra HDRMLT layers", type : 'I' }, 497 | extra_HDRMLT_overdrive: { val: 0, def: 0, name : "Extra HDRMLT overdrive", type : 'R' }, 498 | extra_HDRMLT_iterations: { val: 1, def: 1, name : "Extra HDRMLT iterations", type : 'I' }, 499 | extra_HDRMLT_color: { val: 'None', def: 'None', name : "Extra HDRMLT hue", type : 'S' }, 500 | extra_LHE: { val: false, def: false, name : "Extra LHE", type : 'B' }, 501 | extra_LHE_kernelradius: { val: 110, def: 110, name : "Extra LHE kernel radius", type : 'I' }, 502 | extra_LHE_contrastlimit: { val: 1.3, def: 1.3, name : "Extra LHE contrast limit", type : 'R' }, 503 | extra_LHE_adjusttype: { val: 'Lights', def: 'Lights', name : "Extra LHE adjust type", type : 'S' }, 504 | extra_contrast: { val: false, def: false, name : "Extra contrast", type : 'B' }, 505 | extra_contrast_iterations: { val: 1, def: 1, name : "Extra contrast iterations", type : 'I' }, 506 | extra_auto_contrast: { val: false, def: false, name : "Extra auto contrast", type : 'B' }, 507 | extra_auto_contrast_limit_low: { val: 0.0000, def: 0.0000, name : "Extra auto contrast limit", type : 'R' }, 508 | extra_auto_contrast_limit_high: { val: 100, def: 100, name : "Extra auto contrast limit high", type : 'R' }, 509 | extra_auto_contrast_channels: { val: false, def: false, name : "Extra auto contrast channels", type : 'B' }, 510 | extra_stretch: { val: false, def: false, name : "Extra stretch", type : 'B' }, 511 | extra_autostf: { val: false, def: false, name : "Extra AutoSTF", type : 'B' }, 512 | extra_signature: { val: false, def: false, name : "Extra signature", type : 'B' }, 513 | extra_signature_path: { val: "", def: "", name : "Extra signature path", type : 'S' }, 514 | extra_signature_scale: { val: 0, def: 0, name : "Extra signature scale", type : 'I' }, 515 | extra_signature_position: { val: 'Bottom left', def: 'Bottom left', name : "Extra signature position", type : 'S' }, 516 | extra_shadowclipping: { val: false, def: false, name : "Extra shadow clipping", type : 'B' }, 517 | extra_shadowclippingperc: { val: 0.0001, def: 0.0001, name : "Extra shadow clipping percentage", type : 'R' }, 518 | extra_smoothbackground: { val: false, def: false, name : "Extra smooth background", type : 'B' }, 519 | extra_smoothbackgroundval: { val: 0.01, def: 0.01, name : "Extra smooth background value", type : 'R' }, 520 | extra_smoothbackgroundfactor: { val: 0.5, def: 0.5, name : "Extra smooth background factor", type : 'R' }, 521 | extra_normalize_channels: { val: false, def: false, name : "Extra normalize channels", type : 'B' }, 522 | extra_normalize_channels_reference: { val: 'G', def: 'G', name : "Extra normalize channels reference", type : 'S' }, 523 | extra_normalize_channels_mask: { val: false, def: false, name : "Extra normalize channels mask", type : 'B' }, 524 | extra_normalize_channels_rescale: { val: false, def: false, name : "Extra normalize channels rescale", type : 'B' }, 525 | extra_adjust_channels: { val: false, def: false, name : "Extra adjust channels", type : 'B' }, 526 | extra_adjust_channels_only_k: { val: false, def: false, name : "Extra adjust channels K", type : 'B' }, 527 | extra_adjust_R: { val: 1, def: 1, name : "Extra adjust R", type : 'R' }, 528 | extra_adjust_G: { val: 1, def: 1, name : "Extra adjust G", type : 'R' }, 529 | extra_adjust_B: { val: 1, def: 1, name : "Extra adjust B", type : 'R' }, 530 | extra_force_new_mask: { val: true, def: true, name : "Extra force new mask", type : 'B' }, 531 | extra_range_mask: { val: false, def: false, name : "Extra range_mask", type : 'B' }, 532 | extra_auto_reset: { val: true, def: true, name : "Extra auto reset", type : 'B' }, 533 | extra_shadow_enhance: { val: false, def: false, name : "Extra shadow enhance", type : 'B' }, 534 | extra_highlight_enhance: { val: false, def: false, name : "Extra highlight enhance", type : 'B' }, 535 | extra_gamma: { val: false, def: false, name : "Extra gamma", type : 'B' }, 536 | extra_gamma_value: { val: 1, def: 1, name : "Extra gamma value", type : 'R' }, 537 | 538 | extra_noise_reduction: { val: false, def: false, name : "Extra noise reduction", type : 'B' }, 539 | extra_ACDNR: { val: false, def: false, name : "Extra ACDNR noise reduction", type : 'B' }, 540 | extra_color_noise: { val: false, def: false, name : "Extra color noise reduction", type : 'B' }, 541 | extra_star_noise_reduction: { val: false, def: false, name : "Extra star noise reduction", type : 'B' }, 542 | extra_sharpen: { val: false, def: false, name : "Extra sharpen", type : 'B' }, 543 | extra_sharpen_iterations: { val: 1, def: 1, name : "Extra sharpen iterations", type : 'I' }, 544 | extra_unsharpmask: { val: false, def: false, name : "Extra unsharpmask", type : 'B' }, 545 | extra_unsharpmask_stddev: { val: 2, def: 2, name : "Extra unsharpmask stddev", type : 'I' }, 546 | extra_unsharpmask_amount: { val: 0.8, def: 0.8, name : "Extra unsharpmask amount", type : 'I' }, 547 | extra_highpass_sharpen: { val: false, def: false, name : "Extra highpass sharpen", type : 'B' }, 548 | extra_highpass_sharpen_method: { val: 'Default', def: 'Default', name : "Extra highpass sharpen method", type : 'S' }, 549 | extra_highpass_sharpen_layers: { val: 5, def: 5, name : "Extra highpass sharpen layers", type : 'I' }, 550 | extra_highpass_sharpen_keep_images: { val: false, def: false, name : "Extra highpass sharpen keep images", type : 'B' }, 551 | extra_highpass_sharpen_combine_only: { val: false, def: false, name : "Extra highpass sharpen combine only", type : 'B' }, 552 | extra_highpass_sharpen_noise_reduction: { val: false, def: false, name : "Extra highpass sharpen noise reduction", type : 'B' }, 553 | extra_clarity: { val: false, def: false, name : "Extra clarity", type : 'B' }, 554 | extra_clarity_stddev: { val: 100, def: 100, name : "Extra clarity stddev", type : 'I' }, 555 | extra_clarity_amount: { val: 0.3, def: 0.3, name : "Extra clarity amount", type : 'I' }, 556 | extra_clarity_mask: { val: false, def: false, name : "Extra clarity mask", type : 'B' }, 557 | extra_saturation: { val: false, def: false, name : "Extra saturation", type : 'B' }, 558 | extra_saturation_iterations: { val: 1, def: 1, name : "Extra saturation iterations", type : 'I' }, 559 | extra_smaller_stars: { val: false, def: false, name : "Extra smaller stars", type : 'B' }, 560 | extra_smaller_stars_iterations: { val: 1, def: 1, name : "Extra smaller stars iterations", type : 'I' }, 561 | extra_apply_no_copy_image: { val: false, def: false, name : "Apply no copy image", type : 'B' }, 562 | extra_rotate: { val: false, def: false, name : "Extra rotate", type : 'B' }, 563 | extra_rotate_degrees: { val: '180', def: '180', name : "Extra rotate degrees", type : 'S' }, 564 | extra_color_calibration: { val: false, def: false, name : "Extra color calibration", type : 'B' }, 565 | extra_solve_image: { val: false, def: false, name : "Extra solve image", type : 'B' }, 566 | extra_annotate_image: { val: false, def: false, name : "Extra annotate image", type : 'B' }, 567 | extra_annotate_image_scale: { val: 4, def: 4, name : "Extra annotate image scale", type : 'B' }, 568 | extra_ha_mapping: { val: false, def: false, name : "Extra ha mapping", type : 'B' }, 569 | 570 | // Calibration settings 571 | debayer_pattern: { val: "Auto", def: "Auto", name : "Debayer", type : 'S' }, 572 | banding_reduction: { val: false, def: false, name : "Banding reduction", type : 'B' }, 573 | banding_reduction_protect_highlights: { val: false, def: false, name : "Banding reduction protect highlights", type : 'B' }, 574 | banding_reduction_amount: { val: 1, def: 1, name : "Banding reduction amount", type : 'R' }, 575 | extract_channel_mapping: { val: "None", def: "None", name : "Extract channel mapping", type : 'S' }, 576 | create_superbias: { val: true, def: true, name : "Superbias", type : 'B' }, 577 | bias_master_files: { val: false, def: false, name : "Bias master files", type : 'B' }, 578 | pre_calibrate_darks: { val: false, def: false, name : "Pre-calibrate darks", type : 'B' }, 579 | optimize_darks: { val: true, def: true, name : "Optimize darks", type : 'B' }, 580 | dark_master_files: { val: false, def: false, name : "Dark master files", type : 'B' }, 581 | flat_dark_master_files: { val: false, def: false, name : "Flat dark master files", type : 'B' }, 582 | stars_in_flats: { val: false, def: false, name : "Stars in flats", type : 'B' }, 583 | no_darks_on_flat_calibrate: { val: false, def: false, name : "Do not use darks on flats", type : 'B' }, 584 | lights_add_manually: { val: false, def: false, name : "Add lights manually", type : 'B' }, 585 | integrated_lights: { val: false, def: false, name : "Integrated lights", type : 'B' }, 586 | flats_add_manually: { val: false, def: false, name : "Add flats manually", type : 'B' }, 587 | flatdarks_add_manually: { val: false, def: false, name : "Add flat darks manually", type : 'B' }, 588 | skip_blink: { val: false, def: false, name : "No blink", type : 'B' }, 589 | auto_output_pedestal: { val: true, def: true, name : "Auto output pedestal", type : 'B' }, 590 | output_pedestal: { val: 0, def: 0, name : "Output pedestal", type : 'B' }, 591 | 592 | 593 | // Misc settings 594 | show_flowchart: { val: true, def: true, name : "Show flowchart", type : 'B', skip_reset: true }, 595 | preview_autostf: { val: true, def: true, name : "Preview AutoSTF", type : 'B' }, 596 | preview_resample: { val: false, def: false, name : "Preview resample", type : 'B' }, 597 | preview_resample_target: { val: 2000, def: 2000, name : "Preview resample target", type : 'I' }, 598 | 599 | // Old persistent settings, moved to generic settings 600 | start_with_empty_window_prefix: { val: false, def: false, name: "startWithEmptyPrefixName", type: 'B' }, // Do we always start with empty prefix 601 | use_manual_icon_column: { val: false, def: false, name: "manualIconColumn", type: 'B' } // Allow manual control of icon column 602 | }; 603 | 604 | this.default_startup_image_name = "startup.jpg"; 605 | 606 | /* 607 | Parameters that are persistent and are saved to only Settings and 608 | restored only from Settings at the start. 609 | Note that there is another parameter set par which are saved to 610 | process icon. 611 | */ 612 | this.ppar = { 613 | win_prefix: '', // Current active window name prefix 614 | autocontinue_win_prefix: '', // AutoContinue window name prefix 615 | prefixArray: [], // Array of prefix names and icon count, 616 | // every array element is [icon-column, prefix-name, icon-count] 617 | userColumnCount: -1, // User set column position, if -1 use automatic column position 618 | lastDir: '', // Last save or load dir, used as a default when dir is unknown 619 | savedVersion: "", // Saved version of the script 620 | preview: { 621 | use_preview: true, // Show image preview on dialog preview window 622 | side_preview_visible: true, // Show image preview on the side of the dialog 623 | use_large_preview: true, // Use large preview window 624 | preview_sizes: [], // Array of [ screen_size, width, height ] 625 | preview_width: 0, // Current preview width, default set in getPreviewSize. 626 | preview_height: 0, // Current preview height, default set in getPreviewSize. 627 | side_preview_width: 0, // Current side preview width, default set in getPreviewSize. 628 | side_preview_height: 0, // Current side preview height, default set in getPreviewSize. 629 | show_histogram: true, // Show histogram in preview window 630 | histogram_height: 0, // Histogram height in preview window, default set in getPreviewSize. 631 | side_histogram_height: 0 // Histogram height in side preview window, default set in getPreviewSize. 632 | }, 633 | use_single_column: false, // show all options in a single column 634 | use_more_tabs: true, // use more tabs for parameters and settings 635 | files_in_tab: true, // show files in a tab 636 | show_startup_image: true, // show startup image 637 | startup_image_name: this.default_startup_image_name 638 | }; 639 | 640 | // Run results for testing 641 | this.run_results = { 642 | processing_steps_file: '', // file where processing steps were written 643 | final_image_file: '', // final image file 644 | fatal_error: '' // if non-empty, fatal error during processing 645 | }; 646 | 647 | this.console_hidden = false; // true if console is hidden 648 | 649 | this.debayerPattern_values = [ "Auto", "RGGB", "BGGR", "GBRG", 650 | "GRBG", "GRGB", "GBGR", "RGBG", 651 | "BGRG", "None" ]; 652 | this.debayerPattern_enums = [ Debayer.prototype.Auto, Debayer.prototype.RGGB, Debayer.prototype.BGGR, Debayer.prototype.GBRG, 653 | Debayer.prototype.GRBG, Debayer.prototype.GRGB, Debayer.prototype.GBGR, Debayer.prototype.RGBG, 654 | Debayer.prototype.BGRG, Debayer.prototype.Auto ]; 655 | 656 | this.saved_measurements = null; 657 | this.saved_measurements_sorted = null; 658 | 659 | this.get_flowchart_data = false; // true if we are running in flowchart mode 660 | this.flowchartWindows = []; // array of flowchart window ids 661 | this.flowchartData = null; // flowchart data 662 | this.flowchartActiveId = 0; // active flowchart id 663 | this.flowchartOperationList = []; // array of flowchart operations 664 | this.flowchart_image = null; // flowchart image to save to file 665 | 666 | this.run_auto_continue = false; 667 | this.write_processing_log_file = true; // if we fail very early we set this to false 668 | this.shadow_clip_value = 0.01; 669 | 670 | this.is_gc_process = true; // true if we have GradientCorrection process available 671 | this.is_mgc_process = true; // true if we have MultiscaleGradientCorrection process available 672 | 673 | this.outputRootDir = ""; 674 | this.lightFileNames = null; 675 | this.darkFileNames = null; 676 | this.biasFileNames = null; 677 | this.flatdarkFileNames = null; 678 | this.flatFileNames = null; 679 | this.best_ssweight = 0; 680 | this.best_image = null; 681 | this.user_selected_best_image = null; 682 | this.user_selected_reference_image = []; // array of [image, filter] 683 | this.star_alignment_image = null; 684 | this.exclusion_areas = []; 685 | 686 | 687 | this.subframeselector_call_count = 0; // number of times SubframeSelector was called, for debugging 688 | this.substack_number = 0; 689 | 690 | this.processed_channel_images = []; 691 | 692 | this.extra_target_image = null; 693 | 694 | this.processing_steps = ""; 695 | this.processing_errors = ""; 696 | this.processing_warnings = ""; 697 | this.extra_processing_info = []; // extra processing info steps from last Apply, array of [ txt] 698 | this.all_windows = []; 699 | this.iconPoint = null; 700 | this.iconStartRow = 0; // Starting row for icons, AutoContinue start from non-zero position 701 | 702 | this.lightFilterSet = null; 703 | this.flatFilterSet = null; 704 | this.flatDarkFilterSet = null; 705 | 706 | // These are initialized by util.setDefaultDirs 707 | this.AutoOutputDir = null; 708 | this.AutoCalibratedDir = null; 709 | this.AutoMasterDir = null; 710 | this.AutoProcessedDir = null; 711 | 712 | this.pages = { 713 | LIGHTS : 0, 714 | BIAS : 1, 715 | DARKS : 2, 716 | FLATS : 3, 717 | FLAT_DARKS : 4, 718 | END : 5 719 | }; 720 | 721 | this.columnCount = 0; // A column position 722 | this.haveIconized = 0; 723 | 724 | /* Possible start images for AutoContinue. 725 | * These are used to determine what images are available for AutoContinue. 726 | * The numbering order is important as we use it for comparison. 727 | */ 728 | this.start_images = { 729 | NONE : 0, 730 | RGB : 1, // Integrated RGB color image 731 | L_R_G_B : 2, // Integrated channel images 732 | RGB_GC : 3, // Gradient corrected integrated RGB color image 733 | L_R_G_B_GC : 4, // Gradient corrected integrated channel images 734 | RGB_HT : 5, // Histogram transformed RGB image 735 | L_RGB_HT : 6, // Histogram transformed L+RGB image 736 | FINAL : 7, // Final image 737 | CALIBRATE_ONLY : 8 738 | }; 739 | 740 | this.temporary_windows = []; 741 | 742 | // known window names 743 | this.integration_LRGB_windows = [ 744 | "Integration_L", // must be first 745 | "Integration_R", 746 | "Integration_G", 747 | "Integration_B", 748 | "Integration_H", 749 | "Integration_S", 750 | "Integration_O" 751 | ]; 752 | 753 | this.integration_color_windows = [ 754 | "Integration_RGB" 755 | ]; 756 | 757 | this.integration_data_windows = [ 758 | "LowRejectionMap_ALL", 759 | "AutoBackgroundModel" 760 | ]; 761 | 762 | // Intermediate windows checked before AutoContinue. 763 | // These should not exist before AutoContinue. 764 | this.intermediate_windows = [ 765 | "Integration_L_processed", 766 | "Integration_RGB_processed", 767 | "Integration_RGB_combined", 768 | "Integration_L_HT", 769 | "Integration_RGB_HT", 770 | "Integration_LRGB_HT" 771 | ]; 772 | 773 | this.fixed_windows = [ 774 | "Mapping_L", 775 | "Mapping_R", 776 | "Mapping_G", 777 | "Mapping_B", 778 | "Integration_RGB", 779 | "Integration_L_NB", 780 | "Integration_R_NB", 781 | "Integration_G_NB", 782 | "Integration_B_NB", 783 | "Integration_H_NB", 784 | "Integration_S_NB", 785 | "Integration_O_NB", 786 | // "Integration_RGB_narrowband", 787 | // "Integration_L_start", 788 | // "Integration_RGB_start", 789 | "Integration_L_crop", 790 | "Integration_L_processed", 791 | "Integration_L_NB_processed", 792 | "Integration_RGB_processed", 793 | "Integration_RGB_NB_processed", 794 | "Integration_RGB_combined", 795 | "Integration_L_HT", 796 | "Integration_L_NB_HT", 797 | "Integration_RGB_HT", 798 | "Integration_RGB_NB_HT", 799 | "copy_Integration_RGB_HT", 800 | "copy_Integration_RGB_NB_HT", 801 | "Integration_LRGB_HT", 802 | "Integration_LRGB_NB_HT", 803 | "copy_Integration_LRGB_HT", 804 | "copy_Integration_LRGB_NB_HT", 805 | "AutoMask", 806 | "AutoStarMask", 807 | "AutoStarFixMask", 808 | "SubframeSelector", 809 | "Measurements", 810 | "Expressions", 811 | "L_win_mask", 812 | "Integration_L_map_processed", 813 | "Integration_L_map_HT", 814 | "Integration_L_map_pm_processed", 815 | "Integration_L_map_pm_noGC", 816 | "Integration_L_map_pm_HT" 817 | ]; 818 | 819 | this.calibrate_windows = [ 820 | "AutoMasterBias", 821 | "AutoMasterSuperBias", 822 | "AutoMasterFlatDark_L", 823 | "AutoMasterFlatDark_R", 824 | "AutoMasterFlatDark_G", 825 | "AutoMasterFlatDark_B", 826 | "AutoMasterFlatDark_H", 827 | "AutoMasterFlatDark_S", 828 | "AutoMasterFlatDark_O", 829 | "AutoMasterFlatDark_C", 830 | "AutoMasterDark", 831 | "AutoMasterFlat_L", 832 | "AutoMasterFlat_R", 833 | "AutoMasterFlat_G", 834 | "AutoMasterFlat_B", 835 | "AutoMasterFlat_H", 836 | "AutoMasterFlat_S", 837 | "AutoMasterFlat_O", 838 | "AutoMasterFlat_C" 839 | ]; 840 | 841 | /* Final processed window names, depending on input data and options used. 842 | * These may have Drizzle prefix if that option is used. 843 | */ 844 | this.final_windows = [ 845 | "AutoLRGB", 846 | "AutoRRGB", 847 | "AutoRGB", 848 | "AutoMono" 849 | ]; 850 | 851 | this.test_image_ids = []; // Test images 852 | 853 | // Available narrowband palettes. 854 | // Description of the fields 855 | // name - name of the palette: 856 | // R, G, B - channel expressions for the palette 857 | // all - If true this palette is created if All is selected in the UI 858 | // checkable - If true then the palette can be checked with multiple mappings in the UI 859 | // sho_mappable - If true the palette can be used in SHO mapping in the extra processing 860 | // stretched - True if the palette should be stretched before combing to RGB, like dynamic palettes 861 | this.narrowBandPalettes = [ 862 | { name: "Auto", R: "Auto", G: "Auto", B: "Auto", all: false, checkable: false, sho_mappable: false, stretched: false }, 863 | { name: "SHO", R: "S", G: "H", B: "O", all: true, checkable: true, sho_mappable: false, stretched: false }, 864 | { name: "HOS", R: "H", G: "O", B: "S", all: true, checkable: true, sho_mappable: true, stretched: false }, 865 | { name: "HSO", R: "H", G: "S", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 866 | { name: "OHS", R: "O", G: "H", B: "S", all: true, checkable: true, sho_mappable: true, stretched: false }, 867 | { name: "HOO", R: "H", G: "O", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 868 | { name: "Pseudo RGB", R: "0.75*H + 0.25*S", G: "0.50*S + 0.50*O", B: "0.30*H + 0.70*O", all: true, checkable: true, sho_mappable: true, stretched: false }, 869 | { name: "Natural HOO", R: "H", G: "0.8*O+0.2*H", B: "0.85*O + 0.15*H", all: true, checkable: true, sho_mappable: true, stretched: false }, 870 | { name: "3-channel HOO", R: "0.76*H+0.24*S", G: "O", B: "0.85*O + 0.15*H", all: true, checkable: true, sho_mappable: true, stretched: false }, 871 | { name: "Dynamic SHO", R: "(O^~O)*S + ~(O^~O)*H", G: "((O*H)^~(O*H))*H + ~((O*H)^~(O*H))*O", B: "O", all: true, checkable: true, sho_mappable: true, stretched: true }, 872 | { name: "Dynamic HOO", R: "H", G: "((O*H)^~(O*H))*H + ~((O*H)^~(O*H))*O", B: "O", all: true, checkable: true, sho_mappable: true, stretched: true }, 873 | { name: "max(R,H),G,B", R: "max(R, H)", G: "G", B: "B", all: false, checkable: true, sho_mappable: false, stretched: false }, 874 | { name: "max(RGB,HOO)", R: "max(R, H)", G: "max(G, O)", B: "max(B, O)", all: false, checkable: true, sho_mappable: false, stretched: false }, 875 | { name: "HOO Helix", R: "H", G: "(0.4*H)+(0.6*O)", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 876 | { name: "HSO Mix 1", R: "0.4*H + 0.6*S", G: "0.7*H + 0.3*O", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 877 | { name: "HSO Mix 2", R: "0.4*H + 0.6*S", G: "0.4*O + 0.3*H + 0.3*S", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 878 | { name: "HSO Mix 3", R: "0.5*H + 0.5*S", G: "0.15*H + 0.85*O", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 879 | { name: "HSO Mix 4", R: "0.5*H + 0.5*S", G: "0.5*H + 0.5*O", B: "O", all: true, checkable: true, sho_mappable: true, stretched: false }, 880 | { name: "L-eXtreme SHO", R: "H", G: "0.5*H+0.5*max(S,O)", B: "max(S,O)", all: true, checkable: true, sho_mappable: true, stretched: false }, 881 | { name: "RGB", R: "R", G: "G", B: "B", all: false, checkable: false, sho_mappable: false, stretched: false }, 882 | { name: "User defined", R: "", G: "", B: "", all: false, checkable: false, sho_mappable: false, stretched: false }, 883 | { name: "All", R: "All", G: "All", B: "All", all: false, checkable: false, sho_mappable: false, stretched: false } 884 | ]; 885 | 886 | // If narrowBandPalettes is Auto, we use these mappings to create 887 | // the image. 888 | this.narrowbandAutoMapping = [ 889 | { input: ['L','R','G','B','H'], output: "max(R,H),G,B", check_ha_mapping: true, rgb_stars: false }, 890 | { input: ['R','G','B','H'], output: "max(R,H),G,B", check_ha_mapping: true, rgb_stars: false }, 891 | { input: ['H','O'], output: "HOO", check_ha_mapping: false, rgb_stars: false }, 892 | { input: ['L','R','G','B','H','O'], output: "HOO", check_ha_mapping: false, rgb_stars: true }, 893 | { input: ['R','G','B','H','O'], output: "HOO", check_ha_mapping: false, rgb_stars: true }, 894 | { input: ['S','H','O'], output: "SHO", check_ha_mapping: false, rgb_stars: false }, 895 | { input: ['L','R','G','B','S','H','O'], output: "SHO", check_ha_mapping: false, rgb_stars: true }, 896 | { input: ['R','G','B','S','H','O'], output: "SHO", check_ha_mapping: false, rgb_stars: true } 897 | ]; 898 | 899 | this.getDirectoryInfo = function(simple_text) { 900 | var header = "

AutoIntegrate output files go to the following subdirectories:

"; 901 | var info = [ 902 | "AutoProcessed contains processed final images. Also integrated images and log output is here.", 903 | "AutoOutput contains intermediate files generated during processing", 904 | "AutoMaster contains generated master calibration files", 905 | "AutoCalibrated contains calibrated light files" 906 | ]; 907 | if (simple_text) { 908 | return header + "\n" + info.join('\n'); 909 | } else { 910 | return "

" + header + "

" + "
  • " + info.join('
  • ') + "
"; 911 | } 912 | } 913 | 914 | this.ai_use_persistent_module_settings = true; // read some defaults from persistent module settings 915 | this.testmode = false; // true if we are running in test mode 916 | 917 | if (this.autointegrate_version.indexOf("test") > 0) { 918 | this.autointegrateinfo_link = "https://ruuth.xyz/test/AutoIntegrateInfo.html"; 919 | } else { 920 | this.autointegrateinfo_link = "https://ruuth.xyz/AutoIntegrateInfo.html"; 921 | } 922 | 923 | } /* AutoIntegrateGlobal*/ 924 | 925 | AutoIntegrateGlobal.prototype = new Object; 926 | -------------------------------------------------------------------------------- /AutoIntegrateLDD.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Linear Defect Detection from LinearDefectDetection.js script. 4 | 5 | Slightly modifies by Jarmo Ruuth for AutoIntegrate script. 6 | 7 | Copyright (c) 2019 Vicent Peris (OAUV). All Rights Reserved. 8 | 9 | Redistribution and use in both source and binary forms, with or without 10 | modification, is permitted provided that the following conditions are met: 11 | 12 | 1. All redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | 2. All redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | 19 | 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names 20 | of their contributors, may be used to endorse or promote products derived 21 | from this software without specific prior written permission. For written 22 | permission, please contact info@pixinsight.com. 23 | 24 | 4. All products derived from this software, in any form whatsoever, must 25 | reproduce the following acknowledgment in the end-user documentation 26 | and/or other materials provided with the product: 27 | 28 | "This product is based on software from the PixInsight project, developed 29 | by Pleiades Astrophoto and its contributors (https://pixinsight.com/)." 30 | 31 | Alternatively, if that is where third-party acknowledgments normally 32 | appear, this acknowledgment must be reproduced in the product itself. 33 | 34 | THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS 35 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 36 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 37 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS 38 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 39 | EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS 40 | INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE, 41 | DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 42 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 43 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 44 | POSSIBILITY OF SUCH DAMAGE. 45 | 46 | This product is based on software from the PixInsight project, developed 47 | by Pleiades Astrophoto and its contributors (https://pixinsight.com/). 48 | 49 | */ 50 | 51 | function AutoIntegrateLDD(util) 52 | { 53 | 54 | this.__base__ = Object; 55 | this.__base__(); 56 | 57 | function LDDEngine( win, detectColumns, detectPartialLines, 58 | layersToRemove, rejectionLimit, imageShift, 59 | detectionThreshold, partialLineDetectionThreshold ) 60 | { 61 | console.writeln("LDDEngine"); 62 | let WI = new DefineWindowsAndImages( win, detectPartialLines ); 63 | 64 | // Generate the small-scale image by subtracting 65 | // the large-scale components of the image. 66 | MultiscaleIsolation( WI.referenceSSImage, null, layersToRemove ); 67 | 68 | // Build a list of lines in the image. 69 | // This can include entire or partial rows or columns. 70 | if ( layersToRemove < 7 ) 71 | layersToRemove = 7; 72 | let partialLines; 73 | if ( detectPartialLines ) 74 | partialLines = new PartialLineDetection( detectColumns, WI.referenceImageCopy, 75 | layersToRemove - 3, imageShift, 76 | partialLineDetectionThreshold ); 77 | 78 | let maxPixelPara, maxPixelPerp; 79 | if ( detectColumns ) 80 | { 81 | maxPixelPara = WI.referenceImage.height - 1; 82 | maxPixelPerp = WI.referenceImage.width - 1; 83 | } 84 | else 85 | { 86 | maxPixelPara = WI.referenceImage.width - 1; 87 | maxPixelPerp = WI.referenceImage.height - 1; 88 | } 89 | 90 | let lines; 91 | if ( detectPartialLines ) 92 | lines = new LineList( true, 93 | partialLines.columnOrRow, 94 | partialLines.startPixel, 95 | partialLines.endPixel, 96 | maxPixelPara, maxPixelPerp ); 97 | else 98 | lines = new LineList( true, [], [], [], maxPixelPara, maxPixelPerp ); 99 | 100 | // Calculate the median value of each line in the image. 101 | // Create a model image with the lines filled 102 | // by their respective median values. 103 | console.writeln( "
Analyzing " + lines.columnOrRow.length + " lines in the image
" ); 104 | let lineValues = new Array; 105 | for ( let i = 0; i < lines.columnOrRow.length; ++i ) 106 | { 107 | let lineRect; 108 | if ( detectColumns ) 109 | { 110 | lineRect = new Rect( 1, lines.endPixel[i] - lines.startPixel[i] + 1 ); 111 | lineRect.moveTo( lines.columnOrRow[i], lines.startPixel[i] ); 112 | } 113 | else 114 | { 115 | lineRect = new Rect( lines.endPixel[i] - lines.startPixel[i] + 1, 1 ); 116 | lineRect.moveTo( lines.startPixel[i], lines.columnOrRow[i] ); 117 | } 118 | 119 | let lineStatistics = new IterativeStatistics( WI.referenceSSImage, lineRect, rejectionLimit ); 120 | WI.lineModelImage.selectedRect = lineRect; 121 | WI.lineModelImage.apply( lineStatistics.median ); 122 | lineValues.push( lineStatistics.median ); 123 | } 124 | WI.referenceSSImage.resetSelections(); 125 | WI.lineModelImage.resetSelections(); 126 | 127 | // Build the detection map image 128 | // and the list of detected line defects. 129 | this.detectedColumnOrRow = new Array; 130 | this.detectedStartPixel = new Array; 131 | this.detectedEndPixel = new Array; 132 | let lineModelMedian = WI.lineModelImage.median(); 133 | let lineModelMAD = WI.lineModelImage.MAD(); 134 | let lineRect; 135 | for ( let i = 0; i < lineValues.length; ++i ) 136 | { 137 | if ( detectColumns ) 138 | { 139 | lineRect = new Rect( 1, lines.endPixel[i] - lines.startPixel[i] + 1 ); 140 | lineRect.moveTo( lines.columnOrRow[i], lines.startPixel[i] ); 141 | } 142 | else 143 | { 144 | lineRect = new Rect( lines.endPixel[i] - lines.startPixel[i] + 1, 1 ); 145 | lineRect.moveTo( lines.startPixel[i], lines.columnOrRow[i] ); 146 | } 147 | 148 | WI.lineDetectionImage.selectedRect = lineRect; 149 | let sigma = Math.abs( lineValues[i] - lineModelMedian ) / ( lineModelMAD * 1.4826 ); 150 | WI.lineDetectionImage.apply( parseInt( sigma ) / ( detectionThreshold + 1 ) ); 151 | if ( sigma >= detectionThreshold ) 152 | { 153 | this.detectedColumnOrRow.push( lines.columnOrRow[i] ); 154 | this.detectedStartPixel.push( lines.startPixel[i] ); 155 | this.detectedEndPixel.push( lines.endPixel[i] ); 156 | } 157 | } 158 | 159 | // Transfer the resulting images to their respective windows. 160 | WI.lineDetectionImage.resetSelections(); 161 | WI.lineDetectionImage.truncate( 0, 1 ); 162 | WI.lineModelImage.apply( WI.referenceImage.median(), ImageOp_Add ); 163 | 164 | WI.lineModelWindow.mainView.beginProcess(); 165 | WI.lineModelWindow.mainView.image.apply( WI.lineModelImage ); 166 | WI.lineModelWindow.mainView.endProcess(); 167 | 168 | WI.lineDetectionWindow.mainView.beginProcess(); 169 | WI.lineDetectionWindow.mainView.image.apply( WI.lineDetectionImage ); 170 | WI.lineDetectionWindow.mainView.endProcess(); 171 | 172 | // Free memory space taken by working images. 173 | WI.referenceImage.free(); 174 | WI.referenceSSImage.free(); 175 | WI.lineModelImage.free(); 176 | WI.lineDetectionImage.free(); 177 | if ( detectPartialLines ) 178 | WI.referenceImageCopy.free(); 179 | util.closeOneWindowById(WI.lineModelWindow.mainView.id); 180 | util.closeOneWindowById(WI.lineDetectionWindow.mainView.id); 181 | util.closeOneWindowById("partial_line_detection"); 182 | } 183 | 184 | // ---------------------------------------------------------------------------- 185 | 186 | /* 187 | * Function to subtract the large-scale components from an image using the 188 | * median wavelet transform. 189 | */ 190 | function MultiscaleIsolation( image, LSImage, layersToRemove ) 191 | { 192 | // Generate the large-scale components image. 193 | // First we generate the array that defines 194 | // the states (enabled / disabled) of the scale layers. 195 | let scales = new Array; 196 | for ( let i = 0; i < layersToRemove; ++i ) 197 | scales.push( 1 ); 198 | 199 | // The scale layers are an array of images. 200 | // We use the medianWaveletTransform. This algorithm is less prone 201 | // to show vertical patterns in the large-scale components. 202 | let multiscaleTransform = new Array; 203 | multiscaleTransform = image.medianWaveletTransform( layersToRemove-1, 0, scales ); 204 | // We subtract the last layer to the image. 205 | // Please note that this image has negative pixel values. 206 | image.apply( multiscaleTransform[layersToRemove-1], ImageOp_Sub ); 207 | // Generate a large-scale component image 208 | // if the respective input image is not null. 209 | if ( LSImage != null ) 210 | LSImage.apply( multiscaleTransform[layersToRemove-1] ); 211 | // Remove the multiscale layers from memory. 212 | for ( let i = 0; i < multiscaleTransform.length; ++i ) 213 | multiscaleTransform[i].free(); 214 | } 215 | 216 | /* 217 | * Function to create a list of vertical or horizontal lines in an image. It 218 | * can combine entire rows or columns and fragmented ones, if an array of 219 | * partial sections is specified in the input par. This list is used to 220 | * input the selected regions in the IterativeStatistics function. 221 | */ 222 | function LineList( correctEntireImage, partialColumnOrRow, partialStartPixel, partialEndPixel, maxPixelPara, maxPixelPerp ) 223 | { 224 | this.columnOrRow = new Array; 225 | this.startPixel = new Array; 226 | this.endPixel = new Array; 227 | 228 | if ( !correctEntireImage ) 229 | { 230 | this.columnOrRow = partialColumnOrRow; 231 | this.startPixel = partialStartPixel; 232 | this.endPixel = partialEndPixel; 233 | } 234 | else 235 | { 236 | if ( partialColumnOrRow.length == 0 ) 237 | partialColumnOrRow.push( maxPixelPerp + 1 ); 238 | 239 | let iPartial = 0; 240 | for ( let i = 0; i <= maxPixelPerp; ++i ) 241 | { 242 | if ( iPartial < partialColumnOrRow.length ) 243 | { 244 | if ( i < partialColumnOrRow[iPartial] && correctEntireImage ) 245 | { 246 | this.columnOrRow.push( i ); 247 | this.startPixel.push( 0 ); 248 | this.endPixel.push( maxPixelPara ); 249 | } 250 | else 251 | { 252 | // Get the partial column or row. 253 | this.columnOrRow.push( partialColumnOrRow[iPartial] ); 254 | this.startPixel.push( partialStartPixel[iPartial] ); 255 | this.endPixel.push( partialEndPixel[iPartial] ); 256 | if ( partialStartPixel[iPartial] > 0 ) 257 | { 258 | this.columnOrRow.push( partialColumnOrRow[iPartial] ); 259 | this.startPixel.push( 0 ); 260 | this.endPixel.push( partialStartPixel[iPartial] - 1 ); 261 | } 262 | if ( partialEndPixel[iPartial] < maxPixelPara ) 263 | { 264 | this.columnOrRow.push( partialColumnOrRow[iPartial] ); 265 | this.startPixel.push( partialEndPixel[iPartial] + 1 ); 266 | this.endPixel.push( maxPixelPara ); 267 | } 268 | // In some cases, there can be more than one section of 269 | // the same column or row in the partial defect list. 270 | // In that case, i (which is the current column or row number) 271 | // shouldn't increase because we are repeating 272 | // the same column or row. 273 | i = partialColumnOrRow[iPartial]; 274 | ++iPartial; 275 | } 276 | } 277 | else if ( correctEntireImage ) 278 | { 279 | this.columnOrRow.push( i ); 280 | this.startPixel.push( 0 ); 281 | this.endPixel.push( maxPixelPara ); 282 | } 283 | } 284 | } 285 | } 286 | 287 | /* 288 | * Function to calculate the median and MAD of a selected image area with 289 | * iterative outlier rejection in the high end of the distribution. Useful to 290 | * reject bright objects in a background-dominated image, especially if the 291 | * input image is the output image of MultiscaleIsolation. 292 | */ 293 | function IterativeStatistics( image, rectangle, rejectionLimit ) 294 | { 295 | image.selectedRect = rectangle; 296 | let formerHighRejectionLimit = 1000; 297 | // The initial currentHighRejectionLimit value is set to 0.99 because 298 | // the global rejection sets the rejected pixels to 1. This way, those 299 | // pixels are already rejected in the first iteration. 300 | let currentHighRejectionLimit = 0.99; 301 | let j = 0; 302 | while ( formerHighRejectionLimit / currentHighRejectionLimit > 1.001 || j < 10 ) 303 | { 304 | // Construct the statistics object to rectangle statistics. 305 | // These statistics are updated with the new high rejection limit 306 | // calculated at the end of the iteration. 307 | let iterativeRectangleStatistics = new ImageStatistics; 308 | let irs = iterativeRectangleStatistics 309 | { 310 | irs.medianEnabled = true; 311 | irs.lowRejectionEnabled = false; 312 | irs.highRejectionEnabled = true; 313 | irs.rejectionHigh = currentHighRejectionLimit; 314 | } 315 | iterativeRectangleStatistics.generate( image ); 316 | this.median = iterativeRectangleStatistics.median; 317 | this.MAD = iterativeRectangleStatistics.mad; 318 | formerHighRejectionLimit = currentHighRejectionLimit; 319 | currentHighRejectionLimit = parseFloat( this.median + ( iterativeRectangleStatistics.mad * 1.4826 * rejectionLimit ) ); 320 | ++j; 321 | } 322 | image.resetSelections(); 323 | } 324 | 325 | /* 326 | * Function to detect defective partial columns or rows in an image. 327 | */ 328 | function PartialLineDetection( detectColumns, image, layersToRemove, imageShift, threshold ) 329 | { 330 | if ( ( detectColumns ? image.height : image.width ) < imageShift * 4 ) 331 | throw new Error( "imageShift parameter too high for the current image size" ); 332 | 333 | 334 | // Create a small-scale component image and its image window. 335 | // SSImage will be the main view of the small-scale component 336 | // image window because we need to apply a 337 | // MorphologicalTransformation instance to it. 338 | this.SSImageWindow = new ImageWindow( image.width, 339 | image.height, 340 | image.numberOfChannels, 341 | 32, true, false, 342 | "partial_line_detection" ); 343 | 344 | // The initial small-scale component image is the input image. 345 | this.SSImage = new Image( image.width, 346 | image.height, 347 | image.numberOfChannels, 348 | image.colorSpace, 349 | image.bitsPerSample, 350 | SampleType_Real ); 351 | 352 | this.SSImage.apply( image ); 353 | 354 | // Subtract the large-scale components to the image. 355 | console.noteln( "
* Isolating small-scale image components..." ); 356 | console.flush(); 357 | MultiscaleIsolation( this.SSImage, null, layersToRemove ); 358 | 359 | // The clipping mask is an image to reject the highlights 360 | // of the processed small-scale component image. The initial 361 | // state of this image is the small-scale component image 362 | // after removing the large-scale components. We simply 363 | // binarize this image at 5 sigmas above the image median. 364 | // This way, the bright structures are white and the rest 365 | // of the image is pure black. We'll use this image 366 | // at the end of the processing. 367 | let clippingMask = new Image( image.width, 368 | image.height, 369 | image.numberOfChannels, 370 | image.colorSpace, 371 | image.bitsPerSample, 372 | SampleType_Real ); 373 | 374 | clippingMask.apply( this.SSImage ); 375 | clippingMask.binarize( clippingMask.MAD() * 5 ); 376 | 377 | // Apply a morphological transformation process 378 | // to the small-scale component image. 379 | // The structuring element is a line in the direction 380 | // of the lines to be detected. 381 | console.noteln( "
* Processing small-scale component image..." ); 382 | console.flush(); 383 | let structure; 384 | if ( detectColumns ) 385 | structure = 386 | [[ 387 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 388 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 389 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 390 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 391 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 392 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 393 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 394 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 395 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 396 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 397 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 398 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 399 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 400 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 401 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 402 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 403 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 404 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 405 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 406 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 407 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 408 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 409 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 410 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 411 | 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 412 | ]]; 413 | else 414 | structure = 415 | [[ 416 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 417 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 418 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 419 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 420 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 421 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 422 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 423 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 424 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 425 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 426 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 427 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 428 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 429 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 430 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 431 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 432 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 433 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 434 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 435 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 436 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 437 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 438 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 439 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 440 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 441 | ]]; 442 | 443 | console.writeln( "Applying morphological median transformation..." ); 444 | console.flush(); 445 | for ( let i = 0; i < 5; ++i ) 446 | this.SSImage.morphologicalTransformation( 4, structure, 0, 0, 1 ); 447 | 448 | // Shift a clone of the small-scale component image 449 | // after the morphological transformation. We then subtract 450 | // the shifted image from its parent image. In the resulting 451 | // image, those linear structures with a sudden change 452 | // of contrast over the column or row will result in a bright 453 | // line at the origin of the defect. This lets us 454 | // to detect the defective partial columns or rows. 455 | let shiftedSSImage = new Image( image.width, 456 | image.height, 457 | image.numberOfChannels, 458 | image.colorSpace, 459 | 32, SampleType_Real ); 460 | 461 | shiftedSSImage.apply( this.SSImage ); 462 | detectColumns ? shiftedSSImage.shiftBy( 0, -imageShift ) 463 | : shiftedSSImage.shiftBy( imageShift, 0 ); 464 | this.SSImage.apply( shiftedSSImage, ImageOp_Sub ); 465 | shiftedSSImage.free(); 466 | 467 | // Subtract again the large-scale components 468 | // of this processed small-scale component image. 469 | // This will give a cleaner result before binarizing. 470 | console.writeln( "Isolating small-scale image components..." ); 471 | console.flush(); 472 | MultiscaleIsolation( this.SSImage, null, layersToRemove - 3 ); 473 | 474 | // Binarize the image to isolate the partial line detection structures. 475 | console.writeln( "Isolating partial line defects..." ); 476 | console.flush(); 477 | let imageMedian = this.SSImage.median(); 478 | let imageMAD = this.SSImage.MAD(); 479 | this.SSImage.binarize( imageMedian + imageMAD*threshold ); 480 | // Now, we subtract the binarized the clipping mask from this processed 481 | // small-scale component image. This removes the surviving linear structures 482 | // coming from bright objects in the image. 483 | this.SSImage.apply( clippingMask, ImageOp_Sub ); 484 | this.SSImage.truncate( 0, 1 ); 485 | 486 | // We apply a closure operation with the same structuring element. 487 | // This process removes short surviving lines coming from 488 | // the image noise while keeping the long ones 489 | console.writeln( "Applying morphological closure transformation..." ); 490 | console.flush(); 491 | this.SSImage.morphologicalTransformation( 2, structure, 0, 0, 1 ); 492 | 493 | // Detect the defective partial rows or columns. We select 494 | // those columns or rows having a minimum number of white pixels. 495 | // The minimum is half of the image shift and it is calculated 496 | // by comparing the mean pixel value to the length of the line. 497 | // Then, we find the maximum position to set the origin of the defect. 498 | // The maximum position is the start of the white line but the origin 499 | // of the defect is the end of the white line. To solve this, 500 | // we first mirror the image. 501 | console.noteln( "
* Detecting partial line defects..." ); 502 | console.flush(); 503 | let maxPixelPerp, maxPixelPara, lineRect; 504 | if ( detectColumns ) 505 | { 506 | this.SSImage.mirrorVertical(); 507 | maxPixelPerp = this.SSImage.width - 1; 508 | maxPixelPara = this.SSImage.height - 1; 509 | lineRect = new Rect( 1, this.SSImage.height ); 510 | } 511 | else 512 | { 513 | this.SSImage.mirrorHorizontal(); 514 | maxPixelPerp = this.SSImage.height - 1; 515 | maxPixelPara = this.SSImage.width - 1; 516 | lineRect = new Rect( this.SSImage.width, 1 ); 517 | } 518 | 519 | this.columnOrRow = new Array; 520 | this.startPixel = new Array; 521 | this.endPixel = new Array; 522 | for ( let i = 0; i <= maxPixelPerp; ++i ) 523 | { 524 | detectColumns ? lineRect.moveTo( i, 0 ) 525 | : lineRect.moveTo( 0, i ); 526 | 527 | var lineMeanPixelValue = this.SSImage.mean( lineRect ); 528 | // The equation at right sets the minimum length of the line 529 | // to trigger a defect detection. 530 | if ( lineMeanPixelValue > ( imageShift / ( ( maxPixelPara + 1 - imageShift * 2 ) * 2 ) ) ) 531 | { 532 | this.columnOrRow.push( i ) 533 | detectColumns ? this.startPixel.push( maxPixelPara - parseInt( this.SSImage.maximumPosition( lineRect ).toArray()[1] ) ) 534 | : this.startPixel.push( maxPixelPara - parseInt( this.SSImage.maximumPosition( lineRect ).toArray()[0] ) ); 535 | this.endPixel.push( maxPixelPara ); 536 | } 537 | } 538 | 539 | detectColumns ? this.SSImage.mirrorVertical() : this.SSImage.mirrorHorizontal(); 540 | 541 | this.SSImageWindow.mainView.beginProcess(); 542 | this.SSImageWindow.mainView.image.apply( this.SSImage ); 543 | this.SSImageWindow.mainView.endProcess(); 544 | this.SSImageWindow.show(); 545 | } 546 | 547 | /* 548 | * These are the image windows and images that will be used by the script 549 | * engine. 550 | */ 551 | function DefineWindowsAndImages( win, detectPartialLines ) 552 | { 553 | // Define the working image windows and images. 554 | this.referenceImageWindow = win; 555 | 556 | this.referenceImage = new Image( this.referenceImageWindow.mainView.image.width, 557 | this.referenceImageWindow.mainView.image.height, 558 | this.referenceImageWindow.mainView.image.numberOfChannels, 559 | this.referenceImageWindow.mainView.image.colorSpace, 560 | 32, SampleType_Real ); 561 | 562 | this.referenceImage.apply( this.referenceImageWindow.mainView.image ); 563 | 564 | if ( detectPartialLines ) 565 | { 566 | this.referenceImageCopy = new Image( this.referenceImageWindow.mainView.image.width, 567 | this.referenceImageWindow.mainView.image.height, 568 | this.referenceImageWindow.mainView.image.numberOfChannels, 569 | this.referenceImageWindow.mainView.image.colorSpace, 570 | 32, SampleType_Real ); 571 | 572 | this.referenceImageCopy.apply( this.referenceImageWindow.mainView.image ); 573 | } 574 | 575 | this.referenceSSImage = new Image( this.referenceImage.width, 576 | this.referenceImage.height, 577 | this.referenceImage.numberOfChannels, 578 | this.referenceImage.colorSpace, 579 | 32, SampleType_Real ); 580 | 581 | this.referenceSSImage.apply( this.referenceImage ); 582 | 583 | this.lineModelWindow = new ImageWindow( this.referenceImage.width, 584 | this.referenceImage.height, 585 | this.referenceImage.numberOfChannels, 586 | 32, true, false, "line_model" ); 587 | 588 | this.lineModelImage = new Image( this.referenceImage.width, 589 | this.referenceImage.height, 590 | this.referenceImage.numberOfChannels, 591 | this.referenceImage.colorSpace, 592 | 32, SampleType_Real ); 593 | 594 | this.lineDetectionWindow = new ImageWindow( this.referenceImage.width, 595 | this.referenceImage.height, 596 | this.referenceImage.numberOfChannels, 597 | 32, true, false, "line_detection" ); 598 | 599 | this.lineDetectionImage = new Image( this.referenceImage.width, 600 | this.referenceImage.height, 601 | this.referenceImage.numberOfChannels, 602 | this.referenceImage.colorSpace, 603 | 32, SampleType_Real ); 604 | } 605 | 606 | /* 607 | * LDDOutput the list of detected lines to console and text file. 608 | */ 609 | function LDDOutput( detectColumns, detectedLines, threshold, outputDir ) 610 | { 611 | console.writeln( "LDDOutput" ); 612 | var defects = []; 613 | if ( detectedLines.detectedColumnOrRow.length > 0 ) 614 | { 615 | console.noteln( "Detected lines" ); 616 | console.noteln( "--------------" ); 617 | for ( let i = 0; i < detectedLines.detectedColumnOrRow.length; ++i ) 618 | { 619 | var oneDefect = 620 | [ 621 | true, // defectEnabled 622 | !detectColumns, // defectIsRow 623 | detectedLines.detectedColumnOrRow[i], // defectAddress 624 | true, // defectIsRange 625 | detectedLines.detectedStartPixel[i], // defectBegin 626 | detectedLines.detectedEndPixel[i] // defectEnd 627 | ]; 628 | if (i == 0) { 629 | console.noteln( oneDefect ); 630 | } 631 | defects[defects.length] = oneDefect; 632 | console.noteln( "detectColumns=" + detectColumns + " " + 633 | detectedLines.detectedColumnOrRow[i] + " " + 634 | detectedLines.detectedStartPixel[i] + " " + 635 | detectedLines.detectedEndPixel[i] ); 636 | } 637 | console.noteln( "Detected defect lines: " + detectedLines.detectedColumnOrRow.length ); 638 | } 639 | else 640 | { 641 | console.warningln( "No defect was detected. Try lowering the threshold value." ); 642 | } 643 | return defects; 644 | } 645 | 646 | this.LDDEngine = LDDEngine; 647 | this.LDDOutput = LDDOutput; 648 | 649 | } /* AutoIntegrateLDD */ 650 | 651 | AutoIntegrateLDD.prototype = new Object; 652 | -------------------------------------------------------------------------------- /AutoIntegrateMetricsVisualizer.js: -------------------------------------------------------------------------------- 1 | // AutoIntegrate Astro Image Metrics Visualizer Dialog 2 | // For SubFrame Selector metrics visualization 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define WINDOW_TITLE "AutoIntegrate Metrics Visualizer" 12 | 13 | function AutoIntegrateMetricsVisualizer(global) 14 | { 15 | 16 | this.__base__ = Object; 17 | this.__base__(); 18 | 19 | var dialog; 20 | 21 | // Sample data structure - replace with your actual metrics arrays 22 | var metricsData = [ 23 | { name: "fwhm", data: [2.1, 2.3, 1.8, 2.5, 2.0, 2.2, 1.9, 2.4, 2.1, 1.7], limit: 0.0, filter_high: true }, 24 | { name: "eccentricity", data: [0.15, 0.22, 0.18, 0.28, 0.12, 0.19, 0.16, 0.25, 0.14, 0.11], limit: 0.0, filter_high: true }, 25 | { name: "snr", data: [45.2, 38.7, 52.1, 33.8, 48.9, 41.3, 46.7, 35.2, 49.8, 54.3], limit: 0.0, filter_high: true }, 26 | { name: "stars", data: [0.85, 0.72, 0.91, 0.65, 0.88, 0.78, 0.83, 0.69, 0.87, 0.93], limit: 0.0, filter_high: false } 27 | ]; 28 | 29 | var metricsFilteredOut = []; 30 | var alwaysFilteredOut = []; // Input metrics that are always filtered out 31 | var numberOfDataSets = 0; 32 | 33 | // Custom plotting control 34 | function PlotControl(parent, metrics, stats, color) { 35 | this.__base__ = Control; 36 | this.__base__(parent); 37 | 38 | this.metrics = metrics; 39 | this.title = metrics.name; 40 | this.data = metrics.data; 41 | this.filter_high = metrics.filter_high; 42 | this.plotColor = color || 0xFF0066CC; // Default blue 43 | this.margin = 40; 44 | this.preferredWidth = 500; 45 | this.preferredHeight = 250; 46 | this.plot_limit = metrics.limit || 0.0; 47 | 48 | this.toolTip = "

Plot for " + this.title + " metric.
" + 49 | "Set a limit to filter out values.
" + 50 | "Current limit: " + this.plot_limit.toFixed(8) + 51 | (this.filter_high ? " (Max)" : " (Min)") + "

" + 52 | "

Gray points indicate values filtered out by outliers.
" + 53 | "White points indicate values filtered out by the current limits.

" + 54 | "

Green lines indicate one sigma limits.
" + 55 | "Red lines indicate two sigma limits.

"; 56 | 57 | if (this.title == 'None') { 58 | this.enabled = false; 59 | } 60 | 61 | this.setFixedSize(this.preferredWidth, this.preferredHeight); 62 | 63 | // Calculate data statistics 64 | this.minValue = Math.min.apply(Math, this.data); 65 | this.maxValue = Math.max.apply(Math, this.data); 66 | this.range = this.maxValue - this.minValue; 67 | 68 | this.onPaint = function(x0, y0, x1, y1) { 69 | // console.writeln("PlotControl onPaint: " + this.title + " plot_limit " + this.plot_limit); 70 | var graphics = new Graphics(this); 71 | graphics.fillRect(this.boundsRect, new Brush(0xFF2D2D30)); // Dark background 72 | 73 | // Calculate plot area 74 | var plotX = this.margin; 75 | var plotY = this.margin; 76 | var plotWidth = this.width - 2 * this.margin; 77 | var plotHeight = this.height - 2 * this.margin; 78 | 79 | // Draw plot background 80 | graphics.fillRect(new Rect(plotX, plotY, plotX + plotWidth, plotY + plotHeight), 81 | new Brush(0xFF1E1E1E)); 82 | 83 | // Draw border 84 | graphics.pen = new Pen(0xFF404040, 1); 85 | graphics.drawRect(plotX, plotY, plotX + plotWidth, plotY + plotHeight); 86 | 87 | // Draw title 88 | graphics.pen = new Pen(0xFFFFFFFF); 89 | graphics.font = new Font("Arial", 12); 90 | var titleWidth = graphics.font.width(this.title); 91 | graphics.drawText((this.width - titleWidth) / 2, 20, this.title); 92 | 93 | // Draw grid lines 94 | graphics.pen = new Pen(0xFF404040, 1); 95 | for (var i = 1; i < 5; i++) { 96 | var y = plotY + (plotHeight * i / 5); 97 | graphics.drawLine(plotX, y, plotX + plotWidth, y); 98 | } 99 | 100 | // Draw one sigma and two sigma lines at the top and bottom 101 | if (this.range > 0) { 102 | // Draw upper limits 103 | var oneSigma = stats.mean + stats.stdDev; 104 | var twoSigma = stats.mean + (stats.stdDev * 2); 105 | var oneSigmaY = plotY + plotHeight - (plotHeight * (oneSigma - this.minValue) / this.range); 106 | var twoSigmaY = plotY + plotHeight - (plotHeight * (twoSigma - this.minValue) / this.range); 107 | if (oneSigmaY > plotY) { 108 | graphics.pen = new Pen(0xFF008000, 1); // Green for one sigma 109 | graphics.drawLine(plotX, oneSigmaY, plotX + plotWidth, oneSigmaY); 110 | } 111 | if (twoSigmaY > plotY) { 112 | graphics.pen = new Pen(0xFFCD5C5C, 1); // Red for two sigma 113 | graphics.drawLine(plotX, twoSigmaY, plotX + plotWidth, twoSigmaY); 114 | } 115 | // Draw lower limits 116 | var oneSigmaLow = stats.mean - stats.stdDev; 117 | var twoSigmaLow = stats.mean - (stats.stdDev * 2); 118 | var oneSigmaLowY = plotY + plotHeight - (plotHeight * (oneSigmaLow - this.minValue) / this.range); 119 | var twoSigmaLowY = plotY + plotHeight - (plotHeight * (twoSigmaLow - this.minValue) / this.range); 120 | if (oneSigmaLowY < plotY + plotHeight) { 121 | graphics.pen = new Pen(0xFF008000, 1); // Green for one sigma 122 | graphics.drawLine(plotX, oneSigmaLowY, plotX + plotWidth, oneSigmaLowY); 123 | } 124 | if (twoSigmaLowY < plotY + plotHeight) { 125 | // Draw two sigma line 126 | graphics.pen = new Pen(0xFFCD5C5C, 1); // Red for two sigma 127 | graphics.drawLine(plotX, twoSigmaLowY, plotX + plotWidth, twoSigmaLowY); 128 | } 129 | } 130 | 131 | // Draw data points and lines 132 | if (this.data.length > 0) { 133 | graphics.pen = new Pen(this.plotColor, 2); 134 | graphics.antialiasing = true; 135 | 136 | var points = []; 137 | var accept_count = 0; 138 | for (var i = 0; i < this.data.length; i++) { 139 | var x = plotX + (plotWidth * i / (this.data.length - 1)); 140 | var normalizedValue = (this.data[i] - this.minValue) / this.range; 141 | var y = plotY + plotHeight - (plotHeight * normalizedValue); 142 | // Count filtering for this metric 143 | if (this.plot_limit == 0.0) { 144 | accept_count++; 145 | } else if (this.filter_high) { 146 | if (this.data[i] <= this.plot_limit) { 147 | accept_count++; 148 | } 149 | } else { 150 | if (this.data[i] >= this.plot_limit) { 151 | accept_count++; 152 | } 153 | } 154 | points.push(new Point(x, y)); 155 | } 156 | 157 | if (0) { 158 | // Draw connecting lines 159 | for (var i = 0; i < points.length - 1; i++) { 160 | graphics.drawLine(points[i], points[i + 1]); 161 | } 162 | } 163 | // Draw data points 164 | for (var i = 0; i < points.length; i++) { 165 | if (alwaysFilteredOut[i]) { 166 | // Gray points always filtered out by outliers 167 | graphics.brush = new Brush(0xffC0C0C0); 168 | } else if (metricsFilteredOut[i]) { 169 | // White points filtered out by limits 170 | graphics.brush = new Brush(0xFFFFFFFF); 171 | } else { 172 | graphics.brush = new Brush(this.plotColor); 173 | } 174 | graphics.fillCircle(points[i], 2); 175 | } 176 | 177 | // Draw value labels 178 | graphics.pen = new Pen(0xFFCCCCCC); 179 | graphics.font = new Font("Arial", 8); 180 | 181 | // Y-axis labels 182 | for (var i = 0; i <= 5; i++) { 183 | if (i == 0) { 184 | var value = this.minValue; 185 | } else if (i == 5) { 186 | var value = this.maxValue; 187 | } else { 188 | // Calculate intermediate values 189 | var value = this.minValue + (this.range * i / 5); 190 | } 191 | // Calculate Y position for label 192 | var y = plotY + plotHeight - (plotHeight * i / 5); 193 | // Adaptive change the number of decimals based on range 194 | if (this.range < 10.0) { 195 | var label = value.toFixed(4); 196 | } else if (this.range < 100.0) { 197 | var label = value.toFixed(3); 198 | } else if (this.range < 1000.0) { 199 | var label = value.toFixed(2); 200 | } else { 201 | var label = value.toFixed(1); 202 | } 203 | // graphics.drawText(5, y - 4, label); 204 | graphics.drawText(5, y, label); 205 | } 206 | 207 | if (0) { 208 | // X-axis labels (frame numbers) 209 | for (var i = 0; i < Math.min(this.data.length, 10); i += Math.max(1, Math.floor(this.data.length / 10))) { 210 | var x = plotX + (plotWidth * i / (this.data.length - 1)); 211 | graphics.drawText(x - 5, plotY + plotHeight + 15, (i + 1).toString()); 212 | } 213 | } 214 | this.dataLabel.text = accept_count + " / " + this.data.length; 215 | } else { 216 | this.dataLabel.text = ""; 217 | } 218 | 219 | graphics.end(); 220 | }; 221 | } 222 | 223 | PlotControl.prototype = new Control; 224 | 225 | // Statistics panel 226 | function StatsControl(parent, title, data) { 227 | this.__base__ = Control; 228 | this.__base__(parent); 229 | 230 | this.title = title; 231 | this.data = data; 232 | // this.preferredHeight = 120; 233 | 234 | if (title == 'None') { 235 | this.enabled = false; 236 | } 237 | 238 | // Calculate statistics 239 | var sum = this.data.reduce(function(a, b) { return a + b; }, 0); 240 | this.mean = sum / this.data.length; 241 | this.min = Math.min.apply(Math, this.data); 242 | this.max = Math.max.apply(Math, this.data); 243 | 244 | var variance = this.data.reduce(function(acc, val) { 245 | return acc + Math.pow(val - this.mean, 2); 246 | }.bind(this), 0) / this.data.length; 247 | this.stdDev = Math.sqrt(variance); 248 | 249 | this.onPaint = function() { 250 | var graphics = new Graphics(this); 251 | graphics.fillRect(this.boundsRect, new Brush(0xFF2D2D30)); 252 | 253 | if (!this.enabled) { 254 | graphics.end(); 255 | return; 256 | } 257 | 258 | graphics.pen = new Pen(0xFFFFFFFF); 259 | graphics.font = new Font("Arial", 8); 260 | 261 | var y = 15; 262 | var lineHeight = 16; 263 | 264 | graphics.drawText(10, y, this.title + " Statistics:"); 265 | y += lineHeight + 5; 266 | 267 | graphics.drawText(10, y, "Mean: " + this.mean.toFixed(8)); 268 | y += lineHeight; 269 | graphics.drawText(10, y, "Min: " + this.min.toFixed(8)); 270 | y += lineHeight; 271 | graphics.drawText(10, y, "Max: " + this.max.toFixed(8)); 272 | y += lineHeight; 273 | graphics.drawText(10, y, "Std Dev: " + this.stdDev.toFixed(8)); 274 | 275 | graphics.end(); 276 | }; 277 | } 278 | 279 | StatsControl.prototype = new Control; 280 | 281 | function newLimitEdit(parent, title, plot) { 282 | var limitEdit = new NumericEdit(parent); 283 | 284 | if (title == 'None') { 285 | limitEdit.enabled = false; 286 | } 287 | 288 | limitEdit.real = true; 289 | limitEdit.textAlignment = TextAlign_Left|TextAlign_VertCenter; 290 | limitEdit.label.text = title + " Limit" + (plot.filter_high ? " (Max)" : " (Min)"); 291 | limitEdit.label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 292 | limitEdit.minWidth = 50; 293 | limitEdit.setPrecision( 8 ); 294 | limitEdit.setRange(0, 1000000); 295 | limitEdit.setValue(plot.plot_limit || 0.0); 296 | limitEdit.plot = plot; 297 | 298 | if (limitEdit.plot.filter_high) { 299 | limitEdit.toolTip = "Set the maximum value for " + title + " metric. Values above this limit will be filtered out."; 300 | } else { 301 | limitEdit.toolTip = "Set the minimum value for " + title + " metric. Values below this limit will be filtered out."; 302 | } 303 | 304 | limitEdit.onValueUpdated = function(value) { 305 | // console.writeln("Limit Updated: " + value + " for " + this.label.text); 306 | this.plot.plot_limit = value; 307 | this.plot.metrics.limit = value; 308 | // Update plot with new limit 309 | dialog.updateData(); 310 | }; 311 | return limitEdit; 312 | } 313 | 314 | function newLabel(parent) 315 | { 316 | var lbl = new Label( parent ); 317 | lbl.text = ""; 318 | lbl.textAlignment = TextAlign_Right|TextAlign_VertCenter; 319 | return lbl; 320 | } 321 | 322 | // Main dialog 323 | function AstroMetricsDialog() { 324 | this.__base__ = Dialog; 325 | this.__base__(); 326 | 327 | this.windowTitle = WINDOW_TITLE; 328 | this.minWidth = 800; 329 | if (numberOfDataSets > 2) { 330 | this.minHeight = 780; 331 | } else { 332 | this.minHeight = 600; 333 | } 334 | 335 | // Create statistics panels 336 | this.data1Stats = new StatsControl(this, metricsData[0].name, metricsData[0].data); 337 | this.data2Stats = new StatsControl(this, metricsData[1].name, metricsData[1].data); 338 | this.data3Stats = new StatsControl(this, metricsData[2].name, metricsData[2].data); 339 | this.data4Stats = new StatsControl(this, metricsData[3].name, metricsData[3].data); 340 | 341 | // Create plot controls for each metric 342 | this.data1Plot = new PlotControl(this, metricsData[0], this.data1Stats, 0xFF00AA00); 343 | this.data2Plot = new PlotControl(this, metricsData[1], this.data2Stats, 0xFFFF6600); 344 | this.data3Plot = new PlotControl(this, metricsData[2], this.data3Stats, 0xFF0066FF); 345 | this.data4Plot = new PlotControl(this, metricsData[3], this.data4Stats, 0xFFCC0066); 346 | 347 | // Edit fields for limits 348 | this.data1LimitEdit = newLimitEdit(this, metricsData[0].name, this.data1Plot); 349 | this.data2LimitEdit = newLimitEdit(this, metricsData[1].name, this.data2Plot); 350 | this.data3LimitEdit = newLimitEdit(this, metricsData[2].name, this.data3Plot); 351 | this.data4LimitEdit = newLimitEdit(this, metricsData[3].name, this.data4Plot); 352 | 353 | this.data1Label = newLabel(this); 354 | this.data1Plot.dataLabel = this.data1Label; 355 | this.data2Label = newLabel(this); 356 | this.data2Plot.dataLabel = this.data2Label; 357 | this.data3Label = newLabel(this); 358 | this.data3Plot.dataLabel = this.data3Label; 359 | this.data4Label = newLabel(this); 360 | this.data4Plot.dataLabel = this.data4Label; 361 | 362 | // Sizer for the edit fields 363 | this.data1LimitEditSizer = new HorizontalSizer; 364 | this.data1LimitEditSizer.addSpacing(4); 365 | this.data1LimitEditSizer.add(this.data1LimitEdit); 366 | this.data1LimitEditSizer.addSpacing(20); 367 | this.data1LimitEditSizer.add(this.data1Label); 368 | this.data1LimitEditSizer.addStretch(); 369 | 370 | this.data2LimitEditSizer = new HorizontalSizer; 371 | this.data2LimitEditSizer.addSpacing(4); 372 | this.data2LimitEditSizer.add(this.data2LimitEdit); 373 | this.data2LimitEditSizer.addSpacing(20); 374 | this.data2LimitEditSizer.add(this.data2Label); 375 | this.data2LimitEditSizer.addStretch(); 376 | 377 | this.data3LimitEditSizer = new HorizontalSizer; 378 | this.data3LimitEditSizer.addSpacing(4); 379 | this.data3LimitEditSizer.add(this.data3LimitEdit); 380 | this.data3LimitEditSizer.addSpacing(20); 381 | this.data3LimitEditSizer.add(this.data3Label); 382 | this.data3LimitEditSizer.addStretch(); 383 | 384 | this.data4LimitEditSizer = new HorizontalSizer; 385 | this.data4LimitEditSizer.addSpacing(4); 386 | this.data4LimitEditSizer.add(this.data4LimitEdit); 387 | this.data4LimitEditSizer.addSpacing(20); 388 | this.data4LimitEditSizer.add(this.data4Label); 389 | this.data4LimitEditSizer.addStretch(); 390 | 391 | this.totalLabel = newLabel(this); 392 | 393 | if (metricsData[1].data.length == 0) { 394 | // If no data for second metric, hide it 395 | this.data2Plot.hide(); 396 | this.data2Stats.hide(); 397 | this.data2LimitEdit.hide(); 398 | } 399 | if (metricsData[2].data.length == 0) { 400 | // If no data for third metric, hide it 401 | this.data3Plot.hide(); 402 | this.data3Stats.hide(); 403 | this.data3LimitEdit.hide(); 404 | } 405 | if (metricsData[3].data.length == 0) { 406 | // If no data for fourth metric, hide it 407 | this.data4Plot.hide(); 408 | this.data4Stats.hide(); 409 | this.data4LimitEdit.hide(); 410 | } 411 | 412 | // Sizer for the total accepted frames 413 | this.totalLabelSizer = new HorizontalSizer; 414 | this.totalLabelSizer.addSpacing(24); 415 | this.totalLabelSizer.add(this.totalLabel); 416 | this.totalLabelSizer.addStretch(); 417 | 418 | // Layout 419 | this.plotsGroupBox = new GroupBox(this); 420 | this.plotsGroupBox.title = "Metrics Visualization"; 421 | this.plotsGroupBox.sizer = new VerticalSizer; 422 | 423 | // Plot 1 with data plot and edit field for limit 424 | this.data1Plotsizer = new VerticalSizer; 425 | this.data1Plotsizer.add(this.data1Plot); 426 | this.data1Plotsizer.addSpacing(4); 427 | this.data1Plotsizer.add(this.data1LimitEditSizer); 428 | 429 | // Plot2 with data plot and edit field for limit 430 | this.data2Plotsizer = new VerticalSizer; 431 | this.data2Plotsizer.add(this.data2Plot); 432 | this.data2Plotsizer.addSpacing(4); 433 | this.data2Plotsizer.add(this.data2LimitEditSizer); 434 | 435 | // Plot3 with data plot and edit field for limit 436 | this.data3Plotsizer = new VerticalSizer; 437 | this.data3Plotsizer.add(this.data3Plot); 438 | this.data3Plotsizer.addSpacing(4); 439 | this.data3Plotsizer.add(this.data3LimitEditSizer); 440 | 441 | // Plot4 with data plot and edit field for limit 442 | this.data4Plotsizer = new VerticalSizer; 443 | this.data4Plotsizer.add(this.data4Plot); 444 | this.data4Plotsizer.addSpacing(4); 445 | this.data4Plotsizer.add(this.data4LimitEditSizer); 446 | 447 | // First row of plots 448 | this.topRowSizer = new HorizontalSizer; 449 | this.topRowSizer.addSpacing(20); 450 | this.topRowSizer.add(this.data1Plotsizer); 451 | this.topRowSizer.addSpacing(10); 452 | this.topRowSizer.add(this.data2Plotsizer); 453 | 454 | // Second row of plots 455 | this.bottomRowSizer = new HorizontalSizer; 456 | this.bottomRowSizer.addSpacing(20); 457 | this.bottomRowSizer.add(this.data3Plotsizer); 458 | this.bottomRowSizer.addSpacing(10); 459 | this.bottomRowSizer.add(this.data4Plotsizer); 460 | 461 | this.plotsGroupBox.sizer.add(this.topRowSizer); 462 | this.plotsGroupBox.sizer.addSpacing(10); 463 | this.plotsGroupBox.sizer.add(this.bottomRowSizer); 464 | this.plotsGroupBox.sizer.addSpacing(4); 465 | this.plotsGroupBox.sizer.add(this.totalLabelSizer); 466 | this.plotsGroupBox.sizer.addSpacing(4); 467 | 468 | // Statistics section 469 | this.statsGroupBox = new GroupBox(this); 470 | this.statsGroupBox.title = "Statistics Summary"; 471 | this.statsGroupBox.sizer = new HorizontalSizer; 472 | 473 | this.statsGroupBox.sizer.add(this.data1Stats); 474 | this.statsGroupBox.sizer.add(this.data2Stats); 475 | this.statsGroupBox.sizer.add(this.data3Stats); 476 | this.statsGroupBox.sizer.add(this.data4Stats); 477 | 478 | // Buttons 479 | this.okButton = new PushButton(this); 480 | this.okButton.text = "OK"; 481 | this.okButton.icon = this.scaledResource(":/icons/ok.png"); 482 | this.okButton.onClick = function() { 483 | // Confirm before exiting the dialog to avoid accidental exit with enter key 484 | if ((new MessageBox("Do you really want to close " + WINDOW_TITLE + " and apply limits?", 485 | WINDOW_TITLE, StdIcon_Warning, StdButton_Yes, StdButton_No)).execute() == StdButton_Yes) 486 | { 487 | this.dialog.ok(); 488 | } 489 | }; 490 | 491 | this.cancelButton = new PushButton(this); 492 | this.cancelButton.text = "Cancel"; 493 | this.cancelButton.icon = this.scaledResource(":/icons/cancel.png"); 494 | this.cancelButton.onClick = function() { 495 | this.dialog.cancel(); 496 | }; 497 | 498 | this.buttonsRowSizer = new HorizontalSizer; 499 | this.buttonsRowSizer.addStretch(); 500 | this.buttonsRowSizer.add(this.okButton); 501 | this.buttonsRowSizer.addSpacing(10); 502 | this.buttonsRowSizer.add(this.cancelButton); 503 | 504 | // Main sizer 505 | this.sizer = new VerticalSizer; 506 | this.sizer.margin = 10; 507 | this.sizer.spacing = 10; 508 | this.sizer.add(this.plotsGroupBox); 509 | this.sizer.add(this.statsGroupBox); 510 | this.sizer.add(this.buttonsRowSizer); 511 | 512 | this.adjustToContents(); 513 | } 514 | 515 | AstroMetricsDialog.prototype = new Dialog; 516 | 517 | // Function to update data and refresh dialog 518 | AstroMetricsDialog.prototype.updateData = function() { 519 | 520 | // console.writeln("Updating data"); 521 | 522 | this.updateFilteredOut(); 523 | 524 | // Force repaint 525 | this.data1Plot.repaint(); 526 | this.data2Plot.repaint(); 527 | this.data3Plot.repaint(); 528 | this.data4Plot.repaint(); 529 | }; 530 | 531 | AstroMetricsDialog.prototype.updateFilteredOut = function() { 532 | // console.writeln("Initializing filtering..."); 533 | 534 | metricsFilteredOut = []; 535 | 536 | // Initialize metricsFilteredOut 537 | for (let i = 0; i < metricsData.length; i++) { 538 | for (let j = 0; j < metricsData[i].data.length; j++) { 539 | metricsFilteredOut[j] = false; 540 | } 541 | } 542 | 543 | // Check filtering conditions 544 | for (let i = 0; i < metricsData.length; i++) { 545 | let metric = metricsData[i]; 546 | if (metric.name == 'None') { 547 | // console.writeln("Skipping metric: " + metric.name); 548 | continue; // Skip metrics with name 'None' 549 | } 550 | if (metric.limit == 0.0) { 551 | // console.writeln("No limit set for metric: " + metric.name); 552 | continue; // No filtering if limit is 0 553 | } 554 | // console.writeln("Filtering metric: " + metric.name + " with limit: " + metric.limit); 555 | if (metric.filter_high) { 556 | for (let j = 0; j < metric.data.length; j++) { 557 | if (metric.data[j] > metric.limit) { 558 | metricsFilteredOut[j] = true; 559 | } 560 | } 561 | } else { 562 | for (let j = 0; j < metric.data.length; j++) { 563 | if (metric.data[j] < metric.limit) { 564 | metricsFilteredOut[j] = true; 565 | } 566 | } 567 | } 568 | } 569 | // Count accepted frames 570 | let accepted = 0; 571 | for (let i = 0; i < metricsFilteredOut.length; i++) { 572 | if (!metricsFilteredOut[i] && !alwaysFilteredOut[i]) { 573 | accepted++; 574 | } 575 | } 576 | this.totalLabel.text = "Accepted Frames: " + accepted + " / " + metricsFilteredOut.length; 577 | }; 578 | 579 | // Main execution function 580 | function main(data, filtered_out) { 581 | //console.writeln("Starting AutoIntegrate Metrics Visualizer..."); 582 | 583 | metricsData = data; 584 | alwaysFilteredOut = filtered_out || []; // Input metrics that are always filtered out 585 | 586 | // Count the number of n on-zero data sets 587 | numberOfDataSets = 0; 588 | for (let i = 0; i < metricsData.length; i++) { 589 | if (metricsData[i].data.length > 0) { 590 | numberOfDataSets++; 591 | } 592 | } 593 | 594 | dialog = new AstroMetricsDialog(); 595 | dialog.updateFilteredOut(); 596 | 597 | return dialog.execute(); 598 | } 599 | 600 | this.main = main; 601 | 602 | } /* AutoIntegrateMetricsVisualizer */ 603 | 604 | AutoIntegrateMetricsVisualizer.prototype = new Object; 605 | -------------------------------------------------------------------------------- /AutoIntegratePreview.js: -------------------------------------------------------------------------------- 1 | // Interface function 2 | // 3 | // SetImage = function(image, txt) 4 | // UpdateImage = function(image, txt) 5 | // showClippedImage = function() 6 | // forceRedraw = function() 7 | // setSize = function(w, h) 8 | 9 | /*************************************************************************** 10 | * 11 | * AutoIntegrateMaxPreviewDialog 12 | * 13 | * Show image preview in max size view. 14 | * 15 | */ 16 | function AutoIntegrateMaxPreviewDialog(engine, util, global, image, txt) 17 | { 18 | this.__base__ = Dialog; 19 | this.__base__(); 20 | this.restyle(); 21 | 22 | let sz = util.getScreenSize(this); 23 | 24 | var screen_width = sz[0]; 25 | var screen_height = sz[1]; 26 | 27 | var preview_width = Math.floor(screen_width - 50); 28 | var preview_height = Math.floor(screen_height - 50); 29 | 30 | console.writeln("Maximize image preview: screen_width ", screen_width, ", screen_height ", screen_height + ", preview_width ", preview_width, ", preview_height ", preview_height); 31 | 32 | this.maxPreviewControl = new AutoIntegratePreviewControl(this, "max", engine, util, global, preview_width, preview_height, true); 33 | 34 | this.maxPreviewControl.SetImage(image, txt); 35 | 36 | this.sizer = new VerticalSizer; 37 | this.sizer.margin = 6; 38 | this.sizer.spacing = 4; 39 | this.sizer.add( this.maxPreviewControl ); 40 | 41 | // adjust to ensure that we fit on screen 42 | util.adjustDialogToScreen(this, this.maxPreviewControl, true, preview_width, preview_height); 43 | 44 | this.move(5, 5); // move to top left corner 45 | 46 | this.windowTitle = "Max preview"; 47 | this.adjustToContents(); 48 | this.setFixedSize(); 49 | } 50 | 51 | AutoIntegrateMaxPreviewDialog.prototype = new Dialog; 52 | 53 | /*************************************************************************** 54 | * 55 | * AutoIntegratePreviewControl 56 | * 57 | * Slightly modified by Jarmo Ruuth for AutoIntegrate script. 58 | * 59 | * Copyright (C) 2013, Andres del Pozo 60 | * 61 | * This product is based on software from the PixInsight project, developed 62 | * by Pleiades Astrophoto and its contributors (https://pixinsight.com/). 63 | */ 64 | function AutoIntegratePreviewControl(parentDialog, name, engine, util, global, size_x, size_y, call_from_max_preview) 65 | { 66 | this.__base__ = Frame; 67 | this.__base__(parentDialog); 68 | 69 | this.name = name; 70 | 71 | var par = global.par; 72 | 73 | if (call_from_max_preview) { 74 | this.normalPreview = false; 75 | } else { 76 | this.normalPreview = true; 77 | } 78 | 79 | this.saveNonclippedImage = null; 80 | 81 | // Set image window and bitmap 82 | this.SetImage = function(image, txt) 83 | { 84 | if (par.debug.val) console.writeln(this.name + ":SetImage:image " + image.width + "x" + image.height + ", txt " + txt); 85 | if (this.image) { 86 | this.image.free(); 87 | } 88 | this.image = new Image( image ); 89 | this.bitmap = this.image.render(); 90 | this.scaledBitmap = null; 91 | this.SetZoomOutLimit(); 92 | this.UpdateZoom(-100); 93 | if (txt) { 94 | this.image_name_Label.text = txt; 95 | } else { 96 | this.image_name_Label.text = ""; 97 | } 98 | this.saveNonclippedImage = null; 99 | } 100 | 101 | // Update image window and bitmap 102 | this.UpdateImage = function(image, txt) 103 | { 104 | if (par.debug.val) console.writeln(this.name + ":UpdateImage:image " + image.width + "x" + image.height + ", txt " + txt); 105 | if (this.zoom == this.zoomOutLimit) { 106 | this.SetImage(image, txt); 107 | } else { 108 | if (this.image) { 109 | this.image.free(); 110 | } 111 | this.image = new Image( image ); 112 | this.bitmap = this.image.render(); 113 | this.scaledBitmap = null; 114 | if (this.zoom == this.zoomOutLimit) { 115 | var newZoom = -100; 116 | } else { 117 | var newZoom = this.zoom; 118 | } 119 | this.SetZoomOutLimit(); 120 | this.UpdateZoom(newZoom); 121 | if (txt) { 122 | this.image_name_Label.text = txt; 123 | } else { 124 | this.image_name_Label.text = ""; 125 | } 126 | this.saveNonclippedImage = null; 127 | } 128 | } 129 | 130 | this.showClippedImage = function() 131 | { 132 | if (this.saveNonclippedImage) { 133 | console.writeln("showNonclippedImage"); 134 | this.UpdateImage(this.saveNonclippedImage, this.image_name_Label.text); 135 | return; 136 | } 137 | 138 | console.writeln("showClippedImage"); 139 | 140 | var imgWin = util.findWindow("AutoIntegrate_preview_clipped"); 141 | if (imgWin) { 142 | imgWin.forceClose(); 143 | } 144 | 145 | // Create a new window from the image 146 | imgWin = util.createWindowFromImage(this.image, "AutoIntegrate_preview_clipped"); 147 | 148 | // Show clipped pixels using PixelMath 149 | var P = new PixelMath; 150 | P.expression = "iif( $T <= 0, 0, iif( $T >= 1, 1, 0.5 ) )"; 151 | P.expression1 = ""; 152 | P.expression2 = ""; 153 | P.expression3 = ""; 154 | P.useSingleExpression = true; 155 | P.symbols = ""; 156 | P.clearImageCacheAndExit = false; 157 | P.cacheGeneratedImages = false; 158 | P.generateOutput = true; 159 | P.singleThreaded = false; 160 | P.optimization = true; 161 | P.use64BitWorkingImage = false; 162 | P.rescale = false; 163 | P.rescaleLower = 0; 164 | P.rescaleUpper = 1; 165 | P.truncate = true; 166 | P.truncateLower = 0; 167 | P.truncateUpper = 1; 168 | P.createNewImage = false; 169 | P.showNewImage = false; 170 | P.newImageId = ""; 171 | P.newImageWidth = 0; 172 | P.newImageHeight = 0; 173 | P.newImageAlpha = false; 174 | P.newImageColorSpace = PixelMath.prototype.SameAsTarget; 175 | P.newImageSampleFormat = PixelMath.prototype.SameAsTarget; 176 | /* 177 | * Read-only properties 178 | * 179 | P.outputData = [ // globalVariableId, globalVariableRK, globalVariableG, globalVariableB 180 | ]; 181 | */ 182 | 183 | P.executeOn(imgWin.mainView, false); 184 | 185 | // Save the original non-clipped image 186 | var saveNonclippedImage = this.image; 187 | this.image = null; 188 | 189 | this.UpdateImage(imgWin.mainView.image, this.image_name_Label.text); 190 | 191 | imgWin.forceClose(); 192 | 193 | this.saveNonclippedImage = saveNonclippedImage; 194 | } 195 | 196 | this.UpdateZoom = function (newZoom, refPoint) 197 | { 198 | if (!this.bitmap && !this.image) { 199 | return; 200 | } 201 | if (newZoom < this.zoomOutLimit) { 202 | newZoom = this.zoomOutLimit; 203 | } else if (newZoom >= 1) { 204 | newZoom = 1; 205 | } 206 | if (par.debug.val) console.writeln(this.name + ":UpdateZoom:newZoom " + newZoom); 207 | if (newZoom == this.zoom && this.scaledBitmap) { 208 | return; 209 | } 210 | 211 | if(refPoint==null) { 212 | refPoint=new Point(this.scrollbox.viewport.width/2, this.scrollbox.viewport.height/2); 213 | } 214 | var imgx=null; 215 | if(this.scrollbox.maxHorizontalScrollPosition>0 || this.zoom == this.zoomOutLimit) { 216 | imgx=(refPoint.x+this.scrollbox.horizontalScrollPosition)/this.scale; 217 | } 218 | // imgx and imgy are in this.bitmap coordinates (i.e. 1:1 scale) 219 | var imgy=null; 220 | if(this.scrollbox.maxVerticalScrollPosition>0 || this.zoom == this.zoomOutLimit) { 221 | imgy=(refPoint.y+this.scrollbox.verticalScrollPosition)/this.scale; 222 | } 223 | 224 | this.zoom = newZoom; 225 | this.scale = this.zoom; 226 | this.scaledBitmap = null; 227 | if (this.zoom >= 1) { 228 | this.zoomVal_Label.text = "1:1"; 229 | } else { 230 | this.zoomVal_Label.text = format("1:%d", Math.ceil(1 / this.zoom)); 231 | } 232 | if (this.bitmap) { 233 | if (this.zoom > this.zoomOutLimit) { 234 | if (par.debug.val) console.writeln(this.name + ":UpdateZoom:this.scale " + this.scale); 235 | this.scaledBitmap = this.bitmap.scaled(this.scale); 236 | } else { 237 | if (par.debug.val) console.writeln(this.name + ":UpdateZoom:0.98 * this.scale " + (0.98 * this.scale)); 238 | this.scaledBitmap = this.bitmap.scaled(0.98 * this.scale); 239 | } 240 | } else { 241 | this.scaledBitmap = {width:this.image.width * this.scale, height:this.image.height * this.scale}; 242 | } 243 | this.scrollbox.maxHorizontalScrollPosition = Math.max(0, this.scaledBitmap.width - this.scrollbox.viewport.width); 244 | this.scrollbox.maxVerticalScrollPosition = Math.max(0, this.scaledBitmap.height - this.scrollbox.viewport.height); 245 | 246 | if(this.scrollbox.maxHorizontalScrollPosition>0 && imgx!=null) { 247 | this.scrollbox.horizontalScrollPosition = (imgx*this.scale)-refPoint.x; 248 | } 249 | if(this.scrollbox.maxVerticalScrollPosition>0 && imgy!=null) { 250 | this.scrollbox.verticalScrollPosition = (imgy*this.scale)-refPoint.y; 251 | } 252 | 253 | this.scrollbox.viewport.update(); 254 | } 255 | 256 | this.zoomIn_Button = new ToolButton( this ); 257 | this.zoomIn_Button.icon = this.scaledResource( ":/icons/zoom-in.png" ); 258 | this.zoomIn_Button.setScaledFixedSize( 20, 20 ); 259 | this.zoomIn_Button.toolTip = "Zoom in"; 260 | this.zoomIn_Button.onClick = function() 261 | { 262 | if (par.debug.val) console.writeln(this.parent.name + ":zoom-in"); 263 | this.parent.UpdateZoom(this.parent.zoom + this.parent.zoomOutLimit); 264 | }; 265 | 266 | this.zoomOut_Button = new ToolButton( this ); 267 | this.zoomOut_Button.icon = this.scaledResource( ":/icons/zoom-out.png" ); 268 | this.zoomOut_Button.setScaledFixedSize( 20, 20 ); 269 | this.zoomOut_Button.toolTip = "Zoom out"; 270 | this.zoomOut_Button.onClick = function() 271 | { 272 | if (par.debug.val) console.writeln(this.parent.name + ":zoom-out"); 273 | this.parent.UpdateZoom(this.parent.zoom - this.parent.zoomOutLimit); 274 | }; 275 | 276 | this.zoom11_Button = new ToolButton( this ); 277 | this.zoom11_Button.icon = this.scaledResource( ":/icons/zoom-1-1.png" ); 278 | this.zoom11_Button.setScaledFixedSize( 20, 20 ); 279 | this.zoom11_Button.toolTip = "Zoom 1:1"; 280 | this.zoom11_Button.onClick = function() 281 | { 282 | if (par.debug.val) console.writeln(this.parent.name + ":zoom-1-1"); 283 | this.parent.UpdateZoom(1); 284 | }; 285 | 286 | this.zoomFit_Button = new ToolButton( this ); 287 | this.zoomFit_Button.icon = this.scaledResource( ":/icons/zoom.png" ); 288 | this.zoomFit_Button.setScaledFixedSize( 20, 20 ); 289 | this.zoomFit_Button.toolTip = "Zoom fit"; 290 | this.zoomFit_Button.onClick = function() 291 | { 292 | if (par.debug.val) console.writeln(this.parent.name + ":zoom"); 293 | this.parent.UpdateZoom(-100); 294 | }; 295 | 296 | if (this.normalPreview) { 297 | this.save_Button = new ToolButton( this ); 298 | this.save_Button.icon = this.scaledResource( ":/icons/save-as.png" ); 299 | this.save_Button.setScaledFixedSize( 20, 20 ); 300 | this.save_Button.toolTip = "Save image to a file."; 301 | this.save_Button.onClick = function() 302 | { 303 | if (!this.parent.bitmap) { 304 | console.noteln("No image to save"); 305 | return; 306 | } 307 | let saveFileDialog = new SaveFileDialog(); 308 | saveFileDialog.caption = "Save As TIFF"; 309 | if (global.outputRootDir == "") { 310 | var path = global.ppar.lastDir; 311 | } else { 312 | var path = global.outputRootDir; 313 | } 314 | if (path != "") { 315 | path = util.ensurePathEndSlash(path); 316 | } 317 | saveFileDialog.initialPath = path + "preview" + ".tif"; 318 | saveFileDialog.filters = [["TIFF files", "*.tif"], ["JPEG files", "*.jpg"]]; 319 | if (!saveFileDialog.execute()) { 320 | console.noteln("Preview image not saved"); 321 | return; 322 | } 323 | var copy_win = util.createWindowFromBitmap(this.parent.bitmap, "AutoIntegrate_preview_savetmp"); 324 | console.writeln("save image to ", saveFileDialog.fileName + ", bits ", copy_win.bitsPerSample + ", width ", copy_win.mainView.image.width + ", height ", copy_win.mainView.image.height + ", id ", copy_win.mainView.id); 325 | if (copy_win.bitsPerSample != 16) { 326 | console.writeln("set bits to 16"); 327 | copy_win.setSampleFormat(16, false); 328 | } 329 | // Save image. No format options, no warning messages, 330 | // no strict mode, no overwrite checks. 331 | if (!copy_win.saveAs(saveFileDialog.fileName, false, false, false, false)) { 332 | console.criticalln("Failed to save image: " + saveFileDialog.fileName); 333 | } else { 334 | console.writeln("Saved image: " + saveFileDialog.fileName); 335 | } 336 | util.closeOneWindow(copy_win); 337 | }; 338 | } 339 | if (this.normalPreview) { 340 | this.maxPreview_Button = new ToolButton( this ); 341 | this.maxPreview_Button.icon = this.scaledResource( ":/real-time-preview/full-view.png" ); 342 | this.maxPreview_Button.setScaledFixedSize( 20, 20 ); 343 | this.maxPreview_Button.toolTip = "Open a new dialog to view the image in (almost) full screen size."; 344 | this.maxPreview_Button.onClick = function() 345 | { 346 | let maxPreviewDialog = new AutoIntegrateMaxPreviewDialog(engine, util, global, this.parent.image, this.parent.image_name_Label.text); 347 | maxPreviewDialog.execute(); 348 | gc(false); 349 | }; 350 | } 351 | this.image_name_Label = new Label( this ); 352 | this.image_name_Label.text = ""; 353 | this.image_name_Label.textAlignment = TextAlign_Right | TextAlign_VertCenter; 354 | 355 | this.buttons_Sizer = new HorizontalSizer; 356 | this.buttons_Sizer.add( this.zoomIn_Button ); 357 | this.buttons_Sizer.add( this.zoomOut_Button ); 358 | this.buttons_Sizer.add( this.zoom11_Button ); 359 | this.buttons_Sizer.add( this.zoomFit_Button ); 360 | if (this.normalPreview) { 361 | this.buttons_Sizer.addSpacing( 12 ); 362 | this.buttons_Sizer.add( this.maxPreview_Button ); 363 | } 364 | if (this.normalPreview) { 365 | this.buttons_Sizer.addSpacing( 24 ); 366 | this.buttons_Sizer.add( this.save_Button ); 367 | } 368 | this.buttons_Sizer.addStretch(); 369 | this.buttons_Sizer.addSpacing( 12 ); 370 | this.buttons_Sizer.add( this.image_name_Label ); 371 | 372 | this.zoom = 1; 373 | this.scale = 1; 374 | this.zoomOutLimit = -100; 375 | this.scrollbox = new ScrollBox(this); 376 | this.scrollbox.autoScroll = true; 377 | this.scrollbox.tracking = true; 378 | this.scrollbox.cursor = new Cursor(StdCursor_Arrow); 379 | 380 | this.scroll_Sizer = new HorizontalSizer; 381 | this.scroll_Sizer.add( this.scrollbox ); 382 | 383 | this.SetZoomOutLimit = function() 384 | { 385 | if (par.debug.val) console.writeln(this.name + ":SetZoomOutLimit:width ", this.scrollbox.viewport.width, ", height ", this.scrollbox.viewport.height); 386 | if (par.debug.val) console.writeln(this.name + ":SetZoomOutLimit:image.width ", this.image.width, ", image.height ", this.image.height); 387 | var scaleX = this.scrollbox.viewport.width/this.image.width; 388 | var scaleY = this.scrollbox.viewport.height/this.image.height; 389 | var scale = Math.min(scaleX,scaleY); 390 | this.zoomOutLimit = scale; 391 | if (par.debug.val) console.writeln(this.name + ":SetZoomOutLimit:scale ", scale, ", this.zoomOutLimit ", this.zoomOutLimit); 392 | } 393 | 394 | this.scrollbox.onHorizontalScrollPosUpdated = function (newPos) 395 | { 396 | this.viewport.update(); 397 | } 398 | this.scrollbox.onVerticalScrollPosUpdated = function (newPos) 399 | { 400 | this.viewport.update(); 401 | } 402 | 403 | this.forceRedraw = function() 404 | { 405 | if (par.debug.val) console.writeln(this.name + ":forceRedraw"); 406 | this.scrollbox.viewport.update(); 407 | }; 408 | 409 | this.setSize = function(w, h) 410 | { 411 | if (par.debug.val) console.writeln(this.name + ":setSize"); 412 | this.setScaledMinSize(w, h); 413 | this.width = w; 414 | this.heigth = h; 415 | } 416 | 417 | this.scrollbox.viewport.onMouseWheel = function (x, y, delta, buttonState, modifiers) 418 | { 419 | var preview = this.parent.parent; 420 | preview.UpdateZoom(preview.zoom + (delta > 0 ? preview.zoomOutLimit : -preview.zoomOutLimit), new Point(x,y)); 421 | } 422 | 423 | this.scrollbox.viewport.onClick = function ( x, y, button, buttonState, modifiers ) 424 | { 425 | var preview = this.parent.parent; 426 | var p = preview.transform(x, y, preview); 427 | if(preview.onCustomMouseDown) 428 | { 429 | preview.onCustomMouseDown.call(this, p.x, p.y, button, buttonState, modifiers ) 430 | } 431 | } 432 | 433 | this.scrollbox.viewport.onMouseMove = function ( x, y, buttonState, modifiers ) 434 | { 435 | var preview = this.parent.parent; 436 | 437 | if (!preview.image) { 438 | return; 439 | } 440 | var p = preview.transform(x, y, preview); 441 | 442 | var val = Math.floor(p.x); 443 | if (val < 0) { 444 | val = 0; 445 | } else if (val >= preview.image.width) { 446 | val = preview.image.width - 1; 447 | } 448 | preview.Xval_Label.text = val.toString(); 449 | var val = Math.floor(p.y); 450 | if (val < 0) { 451 | val = 0; 452 | } else if (val >= preview.image.height) { 453 | val = preview.image.height - 1; 454 | } 455 | preview.Yval_Label.text = val.toString(); 456 | try { 457 | var val = preview.image.sample(Math.floor(p.x), Math.floor(p.y)); 458 | preview.SampleVal_Label.text = val.toFixed(5); 459 | } catch(err) { 460 | } 461 | 462 | if(preview.onCustomMouseMove) 463 | { 464 | preview.onCustomMouseMove.call(this, p.x, p.y, buttonState, modifiers ) 465 | } 466 | } 467 | 468 | this.scrollbox.viewport.onMouseRelease = function (x, y, button, buttonState, modifiers) 469 | { 470 | var preview = this.parent.parent; 471 | 472 | var p = preview.transform(x, y, preview); 473 | 474 | if (preview.zoom == 1 && preview.coordinatesEdit) { 475 | preview.coordinatesEdit.text = Math.floor(p.x).toString() + "," + Math.floor(p.y).toString(); 476 | } 477 | 478 | if(preview.onCustomMouseUp) 479 | { 480 | preview.onCustomMouseUp.call(this, p.x, p.y, button, buttonState, modifiers ) 481 | } 482 | } 483 | 484 | this.scrollbox.viewport.onResize = function (wNew, hNew, wOld, hOld) 485 | { 486 | var preview = this.parent.parent; 487 | if (par.debug.val) console.writeln(preview.name + ":onResize"); 488 | if(preview.image && preview.scaledBitmap != null) 489 | { 490 | this.parent.maxHorizontalScrollPosition = Math.max(0, preview.scaledBitmap.width - wNew); 491 | this.parent.maxVerticalScrollPosition = Math.max(0, preview.scaledBitmap.height - hNew); 492 | if (par.debug.val) console.writeln(preview.name + ":onResize, preview.zoom " + preview.zoom + ", preview.zoomOutLimit " + preview.zoomOutLimit); 493 | if (preview.zoom == preview.zoomOutLimit) { 494 | var newZoom = -100; 495 | } else { 496 | var newZoom = preview.zoom; 497 | } 498 | preview.SetZoomOutLimit(); 499 | preview.UpdateZoom(newZoom); 500 | } 501 | this.update(); 502 | } 503 | 504 | this.scrollbox.viewport.onPaint = function (x0, y0, x1, y1) 505 | { 506 | var preview = this.parent.parent; 507 | if (par.debug.val) console.writeln(preview.name + ":onPaint"); 508 | var graphics = new VectorGraphics(this); 509 | 510 | graphics.fillRect(x0,y0, x1, y1, new Brush(0xff202020)); 511 | if (preview.scaledBitmap != null) { 512 | var offsetX = this.parent.maxHorizontalScrollPosition>0 ? -this.parent.horizontalScrollPosition : (this.width-preview.scaledBitmap.width)/2; 513 | var offsetY = this.parent.maxVerticalScrollPosition>0 ? -this.parent.verticalScrollPosition: (this.height-preview.scaledBitmap.height)/2; 514 | graphics.translateTransformation(offsetX, offsetY); 515 | if(preview.bitmap) 516 | graphics.drawBitmap(0, 0, preview.scaledBitmap); 517 | else 518 | graphics.fillRect(0, 0, preview.scaledBitmap.width, preview.scaledBitmap.height, new Brush(0xff000000)); 519 | graphics.pen = new Pen(0xffffffff,0); 520 | graphics.drawRect(-1, -1, preview.scaledBitmap.width + 1, preview.scaledBitmap.height + 1); 521 | } 522 | 523 | if(preview.onCustomPaint) 524 | { 525 | graphics.antialiasing = true; 526 | graphics.scaleTransformation(preview.scale,preview.scale); 527 | preview.onCustomPaint.call(this, graphics, x0, y0, x1, y1); 528 | } 529 | graphics.end(); 530 | } 531 | 532 | this.transform = function(x, y, preview) 533 | { 534 | // if (par.debug.val) console.writeln(this.name + ":transform"); 535 | if (!preview.scaledBitmap) { 536 | return new Point(x, y); 537 | } 538 | var scrollbox = preview.scrollbox; 539 | var ox = 0; 540 | var oy = 0; 541 | ox = scrollbox.maxHorizontalScrollPosition>0 ? -scrollbox.horizontalScrollPosition : (scrollbox.viewport.width-preview.scaledBitmap.width)/2; 542 | oy = scrollbox.maxVerticalScrollPosition>0 ? -scrollbox.verticalScrollPosition: (scrollbox.viewport.height-preview.scaledBitmap.height)/2; 543 | var coordPx = new Point((x - ox) / preview.scale, (y - oy) / preview.scale); 544 | return new Point(coordPx.x, coordPx.y); 545 | } 546 | 547 | this.center = function() 548 | { 549 | var preview = this; 550 | var scrollbox = preview.scrollbox; 551 | var x = scrollbox.viewport.width / 2; 552 | var y = scrollbox.viewport.height / 2; 553 | var p = this.transform(x, y, preview); 554 | return p; 555 | } 556 | 557 | this.zoomLabel_Label =new Label(this); 558 | this.zoomLabel_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 559 | this.zoomLabel_Label.text = "Zoom:"; 560 | this.zoomVal_Label =new Label(this); 561 | this.zoomVal_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 562 | this.zoomVal_Label.text = "1:1"; 563 | 564 | this.Xlabel_Label = new Label(this); 565 | this.Xlabel_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 566 | this.Xlabel_Label .text = "X:"; 567 | this.Xval_Label = new Label(this); 568 | this.Xval_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 569 | this.Xval_Label.text = "---"; 570 | this.Ylabel_Label = new Label(this); 571 | this.Ylabel_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 572 | this.Ylabel_Label.text = "Y:"; 573 | this.Yval_Label = new Label(this); 574 | this.Yval_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 575 | this.Yval_Label.text = "---"; 576 | this.SampleLabel_Label = new Label(this); 577 | this.SampleLabel_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 578 | this.SampleLabel_Label.text = "Val:"; 579 | this.SampleVal_Label = new Label(this); 580 | this.SampleVal_Label.textAlignment = TextAlign_Left|TextAlign_VertCenter; 581 | this.SampleVal_Label.text = "---"; 582 | 583 | if (this.normalPreview) { 584 | this.coordinatesLabel = new Label(this); 585 | this.coordinatesLabel.textAlignment = TextAlign_Left|TextAlign_VertCenter; 586 | this.coordinatesLabel.text = "X,Y:"; 587 | this.coordinatesLabel.toolTip = "Zoom to 1:1 view and click left mouse button to fill coordinates to the coordinates box."; 588 | this.coordinatesEdit = new Edit(this); 589 | this.coordinatesEdit.toolTip = "Zoom to 1:1 view and click left mouse button to fill coordinates to the coordinates box."; 590 | 591 | this.coordinatesCopyFirstButton = new ToolButton( this ); 592 | this.coordinatesCopyFirstButton.icon = parentDialog.scaledResource( ":/icons/left.png" ); 593 | this.coordinatesCopyFirstButton.onClick = function () { 594 | var preview = this.parent.parent; 595 | if (preview.coordinatesEdit.text != "") { 596 | parentDialog.cometAlignFirstXY.text = preview.coordinatesEdit.text; 597 | par.comet_first_xy.val = preview.coordinatesEdit.text; 598 | } 599 | }; 600 | this.coordinatesCopyFirstButton.toolTip = "Copy coordinates to comet first image X₀,Y₀ coordinates."; 601 | 602 | this.coordinatesCopyLastButton = new ToolButton( this ); 603 | this.coordinatesCopyLastButton.icon = parentDialog.scaledResource( ":/icons/right.png" ); 604 | this.coordinatesCopyLastButton.onClick = function () { 605 | var preview = this.parent.parent; 606 | if (preview.coordinatesEdit.text != "") { 607 | parentDialog.cometAlignLastXY.text = preview.coordinatesEdit.text; 608 | par.comet_last_xy.val = preview.coordinatesEdit.text; 609 | } 610 | }; 611 | this.coordinatesCopyLastButton.toolTip = "Copy coordinates to comet last image X₁,Y₁ coordinates."; 612 | } 613 | 614 | this.coords_Frame = new Frame(this); 615 | this.coords_Frame.backgroundColor = 0xffffffff; 616 | this.coords_Frame.sizer = new HorizontalSizer; 617 | this.coords_Frame.sizer.margin = 2; 618 | this.coords_Frame.sizer.spacing = 4; 619 | this.coords_Frame.sizer.add(this.zoomLabel_Label); 620 | this.coords_Frame.sizer.add(this.zoomVal_Label); 621 | this.coords_Frame.sizer.addSpacing(6); 622 | this.coords_Frame.sizer.add(this.Xlabel_Label); 623 | this.coords_Frame.sizer.add(this.Xval_Label); 624 | this.coords_Frame.sizer.addSpacing(6); 625 | this.coords_Frame.sizer.add(this.Ylabel_Label); 626 | this.coords_Frame.sizer.add(this.Yval_Label); 627 | this.coords_Frame.sizer.addSpacing(6); 628 | this.coords_Frame.sizer.add(this.SampleLabel_Label); 629 | this.coords_Frame.sizer.add(this.SampleVal_Label); 630 | if (this.normalPreview) { 631 | this.coords_Frame.sizer.addStretch(); 632 | this.coords_Frame.sizer.add(this.coordinatesLabel); 633 | this.coords_Frame.sizer.add(this.coordinatesEdit); 634 | this.coords_Frame.sizer.add(this.coordinatesCopyFirstButton); 635 | this.coords_Frame.sizer.add(this.coordinatesCopyLastButton); 636 | } 637 | this.coords_Frame.sizer.addStretch(); 638 | this.sizer = new VerticalSizer; 639 | this.sizer.add(this.buttons_Sizer); 640 | this.sizer.add(this.scroll_Sizer); 641 | this.sizer.add(this.coords_Frame); 642 | 643 | var width_overhead = this.scrollbox.viewport.width; 644 | var heigth_overhead = this.scrollbox.viewport.height; 645 | 646 | this.setScaledMinSize(size_x + width_overhead + 6, size_y + heigth_overhead + 6); 647 | } 648 | 649 | AutoIntegratePreviewControl.prototype = new Frame; 650 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AutoIntegrate 2 | 3 | You are welcome to contribute to AutoIntegrate. Your contribution can be for example: 4 | 5 | - Bug report 6 | - Suggestion for a new feature 7 | - Code change like a fix or an enhancement 8 | 9 | ## Report bugs using GitHub 10 | 11 | GitHub issues are used to track bugs. 12 | 13 | ## Use GitHub Pull Requests for Code Changes 14 | 15 | If you want to make a code change please contact me either in https://forums.ruuth.xyz/ or email to astroimagetools@gmail.com. I may have development ongoing so it is better to avoid conflicts in pull requests. 16 | 17 | Steps for using a Pull request 18 | 19 | 1. Fork the repo and create your branch from `master`. 20 | 2. Do your code changes. 21 | 3. Test your change. 22 | 4. Issue a pull request. 23 | 24 | ## Any contributions you make will be under the MIT Software License 25 | 26 | When you submit code changes, your submissions are understood to be under the MIT License. The full AutoIntegrate source code cannot be under MIT License as it contains copyrighted code from other parties like Pleiades Astrophoto. 27 | 28 | ## Use a Consistent Coding Style 29 | 30 | * 4 spaces for indentation rather than tabs 31 | * Try to follow the coding style used in other parts of the software. 32 | 33 | ## References 34 | 35 | This document was adapted from the contribution guidelines from [Transcriptase](https://gist.github.com/briandk/3d2e8b3ec8daf5a27a62) 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoIntegrate 2 | 3 | PixInsight script to process FITS, RAW and other image files. Script automates initial steps of image 4 | processing in PixInsight. 5 | 6 | Script has a GUI interface where some processing options can be selected. 7 | 8 | After running the script there will be integrated light images and automatically processed 9 | final image. LRGB, color/OSC/DSLR and narrowband files are accepted. 10 | 11 | Script can be used for both calibrated and non-calibrated images. More details 12 | of processing the files can be found from the header block of the source code or from 13 | page https://ruuth.xyz/AutoIntegrateInfo.html 14 | 15 | It is possible to automatically install and update AutoIntegrate script by adding it to 16 | the PixInsight update repository. Whenever PixInsight is started it will then check for 17 | updates to AutoIntegrate. To enable automatic updates you need to add the following Url 18 | to the PixInsight Resources/Updates/Manage Repositories. 19 | 20 | https://ruuth.xyz/autointegrate/ 21 | 22 | Alternatively the script can be run from the PixInsight Script Editor. 23 | 24 | Note that starting from AutoIntegrate version 1.56 there is a dependency to ImageSolver script 25 | in PixInsight distribution. This means that ImageSolver files must be in the ../AdP 26 | directory relative to AutoIntegrate directory. You can do this by creating a ../AdP 27 | directory and copying the contents of Pixinsight-install-directory/src/scripts/AdP 28 | there. 29 | 30 | 1. Download source code zip from GitHub and unzip the contents. 31 | 2. Open Script Editor in PixInsight 32 | 3. Open file AutoIntegrate.js 33 | 4. Press F9 to run the script in the editor 34 | 5. Add files with add buttons 35 | 6. Click Run and wait until the script completes 36 | 37 | This product is based on software from the PixInsight project, developed by Pleiades Astrophoto and its contributors (https://pixinsight.com/). 38 | -------------------------------------------------------------------------------- /hue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jarmoruuth/AutoIntegrate/5a1c3c5804b2a59499d7e843439d1f87d73df6e7/hue.png -------------------------------------------------------------------------------- /startup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jarmoruuth/AutoIntegrate/5a1c3c5804b2a59499d7e843439d1f87d73df6e7/startup.jpg --------------------------------------------------------------------------------