1 /*
2 * darks_dialog.cpp
3 * PHD Guiding
4 *
5 * Created by Bruce Waddington in collaboration with David Ault and Andy Galasso
6 * Copyright (c) 2014 Bruce Waddington
7 * All rights reserved.
8 *
9 * This source code is distributed under the following "BSD" license
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * Neither the name of Bret McKee, Dad Dog Development,
18 * Craig Stark, Stark Labs nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 */
35
36 #include "phd.h"
37 #include "darks_dialog.h"
38 #include "wx/valnum.h"
39
40 #include <algorithm>
41 #include <sstream>
42
43 static const int DefDarkCount = 5;
44 static const int DefDMExpTime = 15;
45 static const int DefDMCount = 25;
46
47 static const int MaxNoteLength = 65; // For now
48
49 // Utility function to add the <label, input> pairs to a flexgrid
AddTableEntryPair(wxWindow * parent,wxFlexGridSizer * pTable,const wxString & label,wxWindow * pControl)50 static void AddTableEntryPair(wxWindow *parent, wxFlexGridSizer *pTable, const wxString& label, wxWindow *pControl)
51 {
52 wxStaticText *pLabel = new wxStaticText(parent, wxID_ANY, label + _(": "), wxPoint(-1, -1), wxSize(-1, -1));
53 pTable->Add(pLabel, 1, wxALL, 5);
54 pTable->Add(pControl, 1, wxALL, 5);
55 }
56
NewSpinnerInt(wxWindow * parent,const wxSize & size,int val,int minval,int maxval,int inc,const wxString & tooltip)57 static wxSpinCtrl *NewSpinnerInt(wxWindow *parent, const wxSize& size, int val, int minval, int maxval, int inc,
58 const wxString& tooltip)
59 {
60 wxSpinCtrl *pNewCtrl = pFrame->MakeSpinCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, wxSP_ARROW_KEYS, minval, maxval, val, _("Exposure time"));
61 pNewCtrl->SetValue(val);
62 pNewCtrl->SetToolTip(tooltip);
63 return pNewCtrl;
64 }
65
GetExposureDurationStrings(wxArrayString * ary)66 static void GetExposureDurationStrings(wxArrayString *ary)
67 {
68 std::vector<int> d(pFrame->GetExposureDurations());
69 std::sort(d.begin(), d.end());
70 for (auto it = d.begin(); it != d.end(); ++it)
71 ary->Add(pFrame->ExposureDurationLabel(*it));
72 }
73
MinExposureDefault()74 static wxString MinExposureDefault()
75 {
76 if (pMount && pMount->IsStepGuider())
77 return pFrame->ExposureDurationLabel(100);
78 else
79 return pFrame->ExposureDurationLabel(1000);
80 }
81
MaxExposureDefault()82 static wxString MaxExposureDefault()
83 {
84 if (pMount && pMount->IsStepGuider())
85 return pFrame->ExposureDurationLabel(2500);
86 else
87 return pFrame->ExposureDurationLabel(6000);
88 }
89
90 // Dialog operates in one of two modes: 1) To create a user-requested dark library or 2) To create a master dark frame
91 // and associated data files needed to construct a new defect map
DarksDialog(wxWindow * parent,bool darkLib)92 DarksDialog::DarksDialog(wxWindow *parent, bool darkLib) :
93 wxDialog(parent, wxID_ANY, _("Build Dark Library"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
94 {
95 buildDarkLib = darkLib;
96 if (!buildDarkLib)
97 this->SetTitle(_("Acquire Master Dark Frames for Bad Pixel Map Calculation"));
98 GetExposureDurationStrings(&m_expStrings);
99
100 // Create overall vertical sizer
101 wxBoxSizer *pvSizer = new wxBoxSizer(wxVERTICAL);
102 if (buildDarkLib)
103 {
104 // Dark library controls
105 wxStaticBoxSizer *pDarkGroup = new wxStaticBoxSizer(wxVERTICAL, this, _("Dark Library"));
106 wxFlexGridSizer *pDarkParams = new wxFlexGridSizer(2, 4, 5, 15);
107
108 m_pDarkMinExpTime = new wxComboBox(this, BUTTON_DURATION, wxEmptyString, wxDefaultPosition, wxDefaultSize,
109 m_expStrings, wxCB_READONLY);
110
111 AddTableEntryPair(this, pDarkParams, _("Min Exposure Time"), m_pDarkMinExpTime);
112 m_pDarkMinExpTime->SetValue(pConfig->Profile.GetString("/camera/darks_min_exptime", MinExposureDefault()));
113 m_pDarkMinExpTime->SetToolTip(_("Minimum exposure time for darks. Choose a value corresponding to the shortest camera exposure you will use for guiding."));
114
115 m_pDarkMaxExpTime = new wxComboBox(this, BUTTON_DURATION, wxEmptyString, wxDefaultPosition, wxDefaultSize,
116 m_expStrings, wxCB_READONLY);
117
118 AddTableEntryPair(this, pDarkParams, _("Max Exposure Time"), m_pDarkMaxExpTime);
119 m_pDarkMaxExpTime->SetValue(pConfig->Profile.GetString("/camera/darks_max_exptime", MaxExposureDefault()));
120 m_pDarkMaxExpTime->SetToolTip(_("Maximum exposure time for darks. Choose a value corresponding to the longest camera exposure you will use for guiding."));
121
122 m_pDarkCount = NewSpinnerInt(this, pFrame->GetTextExtent("9999"), pConfig->Profile.GetInt("/camera/darks_num_frames", DefDarkCount),
123 1, 20, 1, _("Number of dark frames for each exposure time"));
124 AddTableEntryPair(this, pDarkParams, _("Frames to take for each \n exposure time"), m_pDarkCount);
125 pDarkGroup->Add(pDarkParams, wxSizerFlags().Border(wxALL, 10));
126 pvSizer->Add(pDarkGroup, wxSizerFlags().Border(wxALL, 10).Expand());
127
128 wxStaticBoxSizer *pBuildOptions = new wxStaticBoxSizer(wxVERTICAL, this, _("Options"));
129 wxBoxSizer *hSizer = new wxBoxSizer(wxHORIZONTAL);
130 wxStaticText *pInfo = new wxStaticText(this, wxID_ANY, wxEmptyString, wxPoint(-1, -1), wxSize(-1, -1));
131 m_rbModifyDarkLib = new wxRadioButton(this, wxID_ANY, _("Modify/extend existing dark library"));
132 m_rbModifyDarkLib->SetToolTip(_("Darks created now will replace older darks having matching exposure times. If different exposure times are used, "
133 "those darks will be added to the library."));
134 m_rbNewDarkLib = new wxRadioButton(this, wxID_ANY, _("Create entirely new dark library"));
135 m_rbNewDarkLib->SetToolTip(_("Darks created now will be used to build a completely new dark library - old dark frames will be discarded. You "
136 " MUST use this option if you've seen alert messages about incompatible frame sizes or mismatches with the current camera."));
137 if (pFrame->DarkLibExists(pConfig->GetCurrentProfileId(), false))
138 {
139 if (pFrame->LoadDarkHandler(true))
140 {
141 double min_v, max_v;
142 int num;
143 pCamera->GetDarklibProperties(&num, &min_v, &max_v);
144 pInfo->SetLabel(wxString::Format(_("Existing dark library covers %d exposure times in the range of %g s to %g s"),
145 num, min_v / 1000., max_v / 1000.));
146 m_rbModifyDarkLib->SetValue(true);
147 }
148 else
149 {
150 pInfo->SetLabel(_("Existing dark library contains incompatible frames - it must be rebuilt from scratch"));
151 m_rbModifyDarkLib->Enable(false);
152 m_rbNewDarkLib->SetValue(true);
153 }
154 }
155 else
156 {
157 pInfo->SetLabel(_("No compatible dark library is available"));
158 m_rbModifyDarkLib->Enable(false);
159 m_rbNewDarkLib->SetValue(true);
160 }
161
162 hSizer->Add(m_rbModifyDarkLib, wxSizerFlags().Border(wxALL, 10));
163 hSizer->Add(m_rbNewDarkLib, wxSizerFlags().Border(wxALL, 10));
164 pBuildOptions->Add(pInfo, wxSizerFlags().Border(wxALL, 10).Border(wxLEFT, 25));
165 pBuildOptions->Add(hSizer, wxSizerFlags().Border(wxALL, 10));
166 pvSizer->Add(pBuildOptions, wxSizerFlags().Expand());
167 }
168 else
169 {
170 // Defect map controls
171 wxStaticBoxSizer *pDMapGroup = new wxStaticBoxSizer(wxVERTICAL, this, _("Dark Frame Settings"));
172 wxFlexGridSizer *pDMapParams = new wxFlexGridSizer(2, 4, 5, 15);
173 m_pDefectExpTime = NewSpinnerInt(this, pFrame->GetTextExtent("9999"), pConfig->Profile.GetInt("/camera/dmap_exptime", DefDMExpTime),
174 5, 15, 1, _("Exposure time for building defect map"));
175 AddTableEntryPair(this, pDMapParams, _("Exposure Time"), m_pDefectExpTime);
176 m_pNumDefExposures = NewSpinnerInt(this, pFrame->GetTextExtent("9999"), pConfig->Profile.GetInt("/camera/dmap_num_frames", DefDMCount),
177 5, 25, 1, _("Number of exposures for building defect map"));
178 AddTableEntryPair(this, pDMapParams, _("Number of Exposures"), m_pNumDefExposures);
179 pDMapGroup->Add(pDMapParams, wxSizerFlags().Border(wxALL, 10));
180 pvSizer->Add(pDMapGroup, wxSizerFlags().Border(wxALL, 10));
181 }
182
183 // Controls for notes and status
184 wxBoxSizer *phSizer = new wxBoxSizer(wxHORIZONTAL);
185 wxStaticText *pNoteLabel = new wxStaticText(this, wxID_ANY, _("Notes: "), wxPoint(-1, -1), wxSize(-1, -1));
186 wxSize sz(38 * StringWidth(this, "M"), -1);
187 m_pNotes = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, sz);
188 m_pNotes->SetToolTip(_("Free-form note, included in FITs header for each dark frame; max length=65"));
189 m_pNotes->SetMaxLength(MaxNoteLength);
190 m_pNotes->SetValue(pConfig->Profile.GetString("/camera/darks_note", ""));
191 phSizer->Add(pNoteLabel, wxSizerFlags().Border(wxALL, 5));
192 phSizer->Add(m_pNotes, wxSizerFlags().Border(wxALL, 5));
193 pvSizer->Add(phSizer, wxSizerFlags().Border(wxALL, 5));
194 phSizer = new wxBoxSizer(wxHORIZONTAL);
195 m_pProgress = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, sz);
196 m_pProgress->Enable(false);
197 pvSizer->Add(phSizer, wxSizerFlags().Border(wxALL, 5));
198 pvSizer->Add(m_pProgress, wxSizerFlags().Border(wxLEFT, 60));
199
200 // Buttons
201 wxBoxSizer *pButtonSizer = new wxBoxSizer( wxHORIZONTAL );
202 m_pResetBtn = new wxButton(this, wxID_ANY, _("Reset"));
203 m_pResetBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DarksDialog::OnReset, this);
204 m_pResetBtn->SetToolTip(_("Reset all parameters to application defaults"));
205
206 m_pStartBtn = new wxButton(this, wxID_ANY, _("Start"));
207 m_pStartBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DarksDialog::OnStart, this);
208 m_pStartBtn->SetToolTip("");
209
210 m_pStopBtn = new wxButton(this, wxID_ANY, _("Cancel"));
211 m_pStopBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DarksDialog::OnStop, this);
212 m_pStopBtn->SetToolTip("");
213
214 pButtonSizer->Add(
215 m_pResetBtn,
216 wxSizerFlags(0).Align(0).Border(wxALL, 10));
217 pButtonSizer->Add(
218 m_pStartBtn,
219 wxSizerFlags(0).Align(0).Border(wxALL, 10));
220 pButtonSizer->Add(
221 m_pStopBtn,
222 wxSizerFlags(0).Align(0).Border(wxALL, 10));
223 pvSizer->Add(pButtonSizer, wxSizerFlags().Center().Border(wxALL, 10));
224
225 // status bar
226 m_pStatusBar = new wxStatusBar(this, -1);
227 m_pStatusBar->SetFieldsCount(1);
228 m_pStatusBar->SetStatusText(_("Set your parameters, click 'Start' to begin"));
229 pvSizer->Add(m_pStatusBar, 0, wxGROW);
230
231 SetAutoLayout(true);
232 SetSizerAndFit (pvSizer);
233
234 m_cancelling = false;
235 m_started = false;
236 }
237
OnStart(wxCommandEvent & evt)238 void DarksDialog::OnStart(wxCommandEvent& evt)
239 {
240 SaveProfileInfo();
241
242 m_pStartBtn->Enable(false);
243 m_pResetBtn->Enable(false);
244 m_pStopBtn->SetLabel(_("Stop"));
245 m_pStopBtn->Refresh();
246 m_started = true;
247 wxYield();
248
249 if (!pCamera->HasShutter)
250 wxMessageBox(_("Cover guide scope"));
251 pCamera->ShutterClosed = true;
252
253 m_pProgress->SetValue(0);
254
255 wxString wrapupMsg;
256
257 bool err = false;
258
259 if (buildDarkLib)
260 {
261 int darkFrameCount = m_pDarkCount->GetValue();
262 int minExpInx = m_pDarkMinExpTime->GetSelection();
263 int maxExpInx = m_pDarkMaxExpTime->GetSelection();
264
265 std::vector<int> exposureDurations(pFrame->GetExposureDurations());
266 std::sort(exposureDurations.begin(), exposureDurations.end());
267
268 int tot_dur = 0;
269 for (int i = minExpInx; i <= maxExpInx; i++)
270 tot_dur += exposureDurations[i] * darkFrameCount;
271
272 m_pProgress->SetRange(tot_dur);
273 if (m_rbNewDarkLib->GetValue()) // User rebuilding from scratch
274 pCamera->ClearDarks();
275
276 for (int inx = minExpInx; inx <= maxExpInx; inx++)
277 {
278 int darkExpTime = exposureDurations[inx];
279 if (darkExpTime >= 1000)
280 ShowStatus (wxString::Format(_("Building master dark at %.1f sec:"), (double)darkExpTime / 1000.0), false);
281 else
282 ShowStatus (wxString::Format(_("Building master dark at %d mSec:"), darkExpTime), false);
283 usImage *newDark = new usImage();
284 err = CreateMasterDarkFrame(*newDark, exposureDurations[inx], darkFrameCount);
285 wxYield();
286 if (m_cancelling || err)
287 {
288 delete newDark;
289 break;
290 }
291 else
292 {
293 pCamera->AddDark(newDark);
294 }
295 }
296
297 if (m_cancelling || err)
298 {
299 ShowStatus(m_cancelling ? _("Operation cancelled - no changes have been made") : _("Operation failed - no changes have been made"), false);
300 if (pFrame->DarkLibExists(pConfig->GetCurrentProfileId(), false))
301 {
302 if (pFrame->LoadDarkHandler(true))
303 Debug.AddLine("Dark library abort, dark library restored.");
304 else
305 Debug.AddLine("Dark library abort, dark library still invalid.");
306 }
307 }
308 else
309 {
310 pFrame->SaveDarkLibrary(m_pNotes->GetValue());
311 pFrame->LoadDarkHandler(true); // Put it to use, including selection of matching dark frame
312 wrapupMsg = _("dark library built");
313 if (m_rbNewDarkLib)
314 Debug.AddLine("Dark library - new dark lib created from scratch.");
315 else
316 Debug.AddLine("Dark library - dark lib modified/extended.");
317 ShowStatus(wrapupMsg, false);
318 }
319 }
320 else
321 {
322 // Start by computing master dark frame with longish exposure times
323 ShowStatus(_("Taking darks to compute defect map: "), false);
324
325 int defectFrameCount = m_pNumDefExposures->GetValue();
326 int defectExpTime = m_pDefectExpTime->GetValue() * 1000;
327
328 m_pProgress->SetRange(defectFrameCount * defectExpTime);
329 m_pProgress->SetValue(0);
330
331 DefectMapDarks darks;
332 err = CreateMasterDarkFrame(darks.masterDark, defectExpTime, defectFrameCount);
333
334 if (m_cancelling)
335 {
336 ShowStatus(_("Operation cancelled"), false);
337 }
338 else if (!err)
339 {
340 // Our role here is to build the dark-related files needed for defect map building
341 ShowStatus(_("Analyzing master dark..."), false);
342
343 // create a median-filtered dark
344 Debug.AddLine("Starting construction of filtered master dark file");
345 darks.BuildFilteredDark();
346 Debug.AddLine("Completed construction of filtered master dark file");
347
348 // save the master dark and the median filtered dark
349 darks.SaveDarks(m_pNotes->GetValue());
350
351 ShowStatus(_("Master dark data files built"), false);
352
353 wrapupMsg = _("Master dark data files built");
354 }
355 }
356
357 m_pStartBtn->Enable(true);
358 m_pResetBtn->Enable(true);
359 pFrame->SetDarkMenuState(); // Hard to know where we are at this point
360
361 if (m_cancelling || err)
362 {
363 m_pProgress->SetValue(0);
364 m_cancelling = false;
365 m_started = false;
366 m_pStopBtn->SetLabel(_("Cancel"));
367 }
368 else
369 {
370 // Put up a message showing results and maybe notice to uncover the scope; then close the dialog
371 pCamera->ShutterClosed = false; // Lights
372 if (!pCamera->HasShutter)
373 wrapupMsg = _("Uncover guide scope") + wxT("\n\n") + wrapupMsg; // Results will appear in smaller font
374 wxMessageBox(wxString::Format(_("Operation complete: %s"), wrapupMsg));
375 EndDialog(wxOK);
376 }
377 }
378
379 // Event handler for dual mode cancel/stop button
OnStop(wxCommandEvent & evt)380 void DarksDialog::OnStop(wxCommandEvent& evt)
381 {
382 if (m_started)
383 {
384 m_cancelling = true;
385 ShowStatus(_("Cancelling..."), false);
386 }
387 else
388 wxDialog::Close();
389 }
390
OnReset(wxCommandEvent & evt)391 void DarksDialog::OnReset(wxCommandEvent& evt)
392 {
393 if (buildDarkLib)
394 {
395 m_pDarkMinExpTime->SetValue(MinExposureDefault());
396 m_pDarkMaxExpTime->SetValue(MaxExposureDefault());
397 m_pDarkCount->SetValue(DefDarkCount);
398 }
399 else
400 {
401 m_pDefectExpTime->SetValue(DefDMExpTime);
402 m_pNumDefExposures->SetValue(DefDMCount);
403 m_pNotes->SetValue("");
404 }
405 }
406
ShowStatus(const wxString msg,bool appending)407 void DarksDialog::ShowStatus(const wxString msg, bool appending)
408 {
409 static wxString preamble;
410
411 if (appending)
412 m_pStatusBar->SetStatusText(preamble + " " + msg);
413 else
414 {
415 m_pStatusBar->SetStatusText(msg);
416 preamble = msg;
417 }
418 }
419
SaveProfileInfo()420 void DarksDialog::SaveProfileInfo()
421 {
422 if (buildDarkLib)
423 {
424 pConfig->Profile.SetString("/camera/darks_min_exptime", m_pDarkMinExpTime->GetValue());
425 pConfig->Profile.SetString("/camera/darks_max_exptime", m_pDarkMaxExpTime->GetValue());
426 pConfig->Profile.SetInt("/camera/darks_num_frames", m_pDarkCount->GetValue());
427 }
428 else
429 {
430 pConfig->Profile.SetInt("/camera/dmap_exptime", m_pDefectExpTime->GetValue());
431 pConfig->Profile.SetInt("/camera/dmap_num_frames", m_pNumDefExposures->GetValue());
432 }
433 pConfig->Profile.SetString("/camera/darks_note", m_pNotes->GetValue());
434 }
435
436 struct Histogram
437 {
438 unsigned long val[256];
439 unsigned int median;
440 double mean;
441
HistogramHistogram442 Histogram(const usImage& img)
443 {
444 memset(&val[0], 0, sizeof(val));
445 mean = 0.0;
446 for (unsigned int i = 0; i < img.NPixels; i++)
447 {
448 unsigned short v = img.ImageData[i];
449 mean += v;
450 v >>= (img.BitsPerPixel - 8);
451 if (v > 255)
452 v = 255; // should never happen if BitsPerPixel is valid
453 ++val[v];
454 }
455 mean /= img.NPixels;
456 // median (approx)
457 unsigned long sum = 0;
458 int i;
459 for (i = 0; i < 256; i++)
460 {
461 sum += val[i];
462 if (sum > img.NPixels / 2)
463 break;
464 }
465 median = i << (img.BitsPerPixel - 8);
466 }
467
DumpHistogram468 void Dump()
469 {
470 Debug.Write(wxString::Format("mean = %.f median(approx) = %u\n", mean, median));
471 int i = 0;
472 for (int l = 0; l < 4; l++)
473 {
474 std::ostringstream os;
475 os << "histo[" << (l * 64) << ".." << ((l + 1) * 64 - 1) << "]";
476 for (int j = 0; j < 64; j++, i++)
477 os << ' ' << val[i];
478 os << "\n";
479 Debug.Write(os.str());
480 }
481 }
482 };
483
CreateMasterDarkFrame(usImage & darkFrame,int expTime,int frameCount)484 bool DarksDialog::CreateMasterDarkFrame(usImage& darkFrame, int expTime, int frameCount)
485 {
486 bool err = false;
487
488 pCamera->InitCapture();
489 darkFrame.ImgExpDur = expTime;
490 darkFrame.ImgStackCnt = frameCount;
491
492 unsigned int *avgimg = 0;
493
494 for (int j = 1; j <= frameCount; j++)
495 {
496 wxYield();
497 if (m_cancelling)
498 break;
499 ShowStatus(wxString::Format(_("Taking dark frame %d/%d"), j, frameCount), true);
500
501 Debug.Write(wxString::Format("Capture dark frame %d/%d exp=%d\n", j, frameCount, expTime));
502 err = GuideCamera::Capture(pCamera, expTime, darkFrame, CAPTURE_DARK);
503 if (err)
504 {
505 ShowStatus(wxString::Format(_("%.1f s dark FAILED"), (double)expTime / 1000.0), true);
506 pCamera->ShutterClosed = false;
507 break;
508 }
509
510 m_pProgress->SetValue(m_pProgress->GetValue() + expTime);
511 wxYield();
512
513 darkFrame.CalcStats();
514
515 Debug.Write(wxString::Format("dark frame stats: bpp %u min %u max %u med %u filtmin %u filtmax %u\n",
516 darkFrame.BitsPerPixel, darkFrame.MinADU, darkFrame.MaxADU,
517 darkFrame.MedianADU, darkFrame.FiltMin, darkFrame.FiltMax));
518
519 Histogram h(darkFrame);
520 h.Dump();
521 wxYield();
522
523 if (!avgimg)
524 {
525 avgimg = new unsigned int[darkFrame.NPixels];
526 memset(avgimg, 0, darkFrame.NPixels * sizeof(*avgimg));
527 }
528
529 unsigned int *iptr = avgimg;
530 const unsigned short *usptr = darkFrame.ImageData;
531 for (unsigned int i = 0; i < darkFrame.NPixels; i++)
532 *iptr++ += *usptr++;
533 }
534
535 if (!m_cancelling && !err)
536 {
537 ShowStatus(_("Dark frames complete"), true);
538 const unsigned int *iptr = avgimg;
539 unsigned short *usptr = darkFrame.ImageData;
540 for (unsigned int i = 0; i < darkFrame.NPixels; i++)
541 *usptr++ = (unsigned short)(*iptr++ / frameCount);
542 }
543
544 m_pProgress->SetValue(m_pProgress->GetValue() + expTime);
545 wxYield();
546
547 delete[] avgimg;
548
549 return err;
550 }
551
~DarksDialog(void)552 DarksDialog::~DarksDialog(void)
553 {
554 }
555