Exemplo n.º 1
0
        // Load  all the jpg images found in the folder
        Boolean LoadDByScanningImageFolder()
        {
            DateTimeHandler dateTimeHandler = new DateTimeHandler();
            FileInfo fileInfo;
            ProgressState progressState = new ProgressState();
            ImageProperties imgprop;  // Collect the image properties for for the 2nd pass...
            List<ImageProperties> imgprop_list = new List<ImageProperties>();
            Dictionary<String, String> dataline = new Dictionary<String, String>();   // Populate the data for the image
            Dictionary<String, String> markerline = new Dictionary<String, String>(); // Populate the markers database, where each key column corresponds to each key counter in the datatable
            int index = 0;
            string datalabel = "";
            bool ambiguous_daymonth_order = false;

            this.imageFilePaths = new DirectoryInfo(this.dbData.FolderPath).GetFiles("*.jpg");
            int count = imageFilePaths.Length;
            if (count == 0) return false;

            // Create the database and its table before we can load any data into it
            // Open a connection to the template DB
            bool result = this.dbData.CreateDB(template);

            // We generate the data user interface controls from the template description after the database has been created from the template
            myControls.GenerateControls(dbData);
            MenuItemControlsInSeparateWindow_Click(this.MenuItemControlsInSeparateWindow, null);  //this.ControlsInMainWindow();

            this.dbData.CreateTables();
            this.dbData.CreateLookupTables();

            // We want to show previews of the frames to the user as they are individually loaded
            // Because WPF uses a scene graph, we have to do this by a background worker, as this forces the update
            var bgw = new BackgroundWorker() { WorkerReportsProgress = true };
            bgw.DoWork += (ow, ea) =>
            {   // this runs on the background thread; its written as an anonymous delegate
                //We need to invoke this to allow updates on the UI
                this.Dispatcher.Invoke(new Action(() =>
                {;
                    // First, change the UI
                    this.helpControl.Visibility = System.Windows.Visibility.Collapsed;
                    Feedback(null, 0, "Examining images...");
                }));

                // First pass: Examine images to extract its basic properties
                BitmapSource bmap;
                BitmapSource corruptedbmp = BitmapFrame.Create(new Uri("pack://application:,,/Resources/corrupted.jpg"));;
                for (int i = 0; i < count; i++)
                {
                    fileInfo = imageFilePaths[i];
                     bmap = null;

                    imgprop = new ImageProperties();
                    imgprop.Name = fileInfo.Name;
                    imgprop.Folder = Utilities.GetFolderNameFromFolderPath(this.FolderPath);
                    try
                    {
                        // Create the bitmap and determine its ImageQuality
                        bmap = BitmapFrame.Create(new Uri(fileInfo.FullName), BitmapCreateOptions.None, BitmapCacheOption.None);

                        bool dark = PixelBitmap.IsDark(bmap, this.darkPixelThreshold, this.darkPixelRatioThreshold);  //
                        imgprop.ImageQuality = (dark) ? (int)Constants.ImageQualityFilters.Dark : (int)Constants.ImageQualityFilters.Ok;
                    }
                    catch
                    {
                        bmap = corruptedbmp;
                        imgprop.ImageQuality = (int) Constants.ImageQualityFilters.Corrupted;
                    }

                    // Get the data from the metadata
                    BitmapMetadata meta = (BitmapMetadata) bmap.Metadata;
                    imgprop.DateMetadata = meta.DateTaken;
                    // For some reason, different versions of Windows treat creation time and modification time differently,
                    // giving inconsisten values. So I just check both and take the lesser of the two.
                    DateTime time1 = File.GetCreationTime(fileInfo.FullName);
                    DateTime time2 = File.GetLastWriteTime(fileInfo.FullName);
                    imgprop.DateFileCreation = (DateTime.Compare (time1, time2) < 0) ? time1 : time2;
                    //string time3 = (meta.DateTaken == null) ? "null" : meta.DateTaken.ToString();

                    //Debug.Print(fileInfo.Name + " " + time1.ToString() + " " + time2.ToString() + " " + time3);
                    imgprop.ID = index + 1; // its plus 1 as the Database IDs start at 1 rather than 0
                    imgprop_list.Add (imgprop);

                    index++;
                    int progress = Convert.ToInt32(Convert.ToDouble(index) / Convert.ToDouble(count) * 100);

                    if (index == 1 || (index % 1 == 0) )
                    {
                        progressState.Message = String.Format ("{0}/{1}: Examining {2}", i, count, imgprop.Name);
                        progressState.Bmap = bmap;
                        bgw.ReportProgress(progress, progressState);
                    }
                    else
                    {
                        progressState.Bmap = null;
                    }
                }

                // Second pass: Determine dates ... This can be pretty quick, so we don't really need to give any feedback on it.
                progressState.Message = "Second pass";
                progressState.Bmap = null;
                bgw.ReportProgress(0, progressState);
                ambiguous_daymonth_order = DateTimeHandler.VerifyAndUpdateDates(imgprop_list);

                // Third pass: Update database
                // TODO This is pretty slow... a good place to make it more efficient by adding multiple values in one shot

                // We need to get a list of which columns are counters vs notes or fixed coices,
                // as we will shortly have to initialize them to some defaults
                List<string> CounterList = new List<string>();
                List<string> Notes_and_FixedChoicesList = new List<string>();
                List<string> FlagsList = new List<string>();
                for (int i = 0; i < this.dbData.dataTable.Columns.Count; i++)
                {
                    datalabel = this.dbData.dataTable.Columns[i].ColumnName;
                    string type = (string)this.dbData.TypeFromKey[datalabel];
                    if (null == type) continue; // Column must be the ID, which we skip over as its not a key.
                    if (type.Equals(Constants.COUNTER)) CounterList.Add(datalabel);
                    else if (type.Equals(Constants.NOTE) || type.Equals(Constants.FIXEDCHOICE)) Notes_and_FixedChoicesList.Add(datalabel);
                    else if (type.Equals(Constants.FLAG)) FlagsList.Add(datalabel);
                }

                // Create a dataline from the image properties, add it to a list of data lines,
                // then do a multiple insert of the list of datalines to the database
                List <Dictionary<string, string>> dataline_list ; //= new List <Dictionary<string, string>> ();
                List<Dictionary<string, string>> markerline_list ; //= new List<Dictionary<string, string>>();
                //for (int i = 0; i < imgprop_list.Count; i++)

                const int interval = 100;
                for (int j = 0; j < imgprop_list.Count; j++)
                {
                    // Create a dataline from the image properties, add it to a list of data lines,
                    // then do a multiple insert of the list of datalines to the database
                    dataline_list = new List<Dictionary<string, string>>();
                    markerline_list = new List<Dictionary<string, string>>();
                    for (int i = j; ( (i < (j + interval)) && (i < imgprop_list.Count) ); i++)
                    {

                        // THE PROBLEM IS THAT WE ARE NOT ADDING THESE VALUES IN THE SAME ORDER AS THE TABLE
                        // THEY MUST BE IN THE SAME ORDER IE, AS IN THE COLUMNS. This case statement just fills up
                        // the dataline in the same order as the template table.
                        // It assumes that the key is always the first column
                        dataline = new Dictionary<string, string>();
                        markerline = new Dictionary<string, string>();
                      //dataline.Add(Constants.ID, "NULL");     // Add the ID. Its Null to force autoincrement
                      //  markerline.Add(Constants.ID, (i+1).ToString());
                        for (int col = 0; col < dbData.dataTable.Columns.Count; col++) // Fill up each column in order
                        {
                            string col_datalabel = dbData.dataTable.Columns[col].ColumnName;
                            string type = (string) dbData.TypeFromKey [col_datalabel];
                            if (null == type) continue; // a null will be returned from the ID, as we don't add it to the typefromkey hash.
                            switch (type)
                            {
                                case Constants.FILE: // Add The File name
                                    datalabel = (string)this.dbData.DataLabelFromType[Constants.FILE];
                                    dataline.Add(datalabel, imgprop_list[i].Name);
                                    break;
                                case Constants.FOLDER: // Add The Folder name
                                    datalabel = (string)this.dbData.DataLabelFromType[Constants.FOLDER];
                                    dataline.Add(datalabel, imgprop_list[i].Folder);
                                    break;
                                case Constants.DATE:
                                    // Add the date
                                    datalabel = (string)this.dbData.DataLabelFromType[Constants.DATE];
                                    dataline.Add(datalabel, imgprop_list[i].FinalDate);
                                    break;
                                case Constants.TIME:
                                    // Add the time
                                    datalabel = (string)this.dbData.DataLabelFromType[Constants.TIME];
                                    dataline.Add(datalabel, imgprop_list[i].FinalTime);
                                    break;
                                case Constants.IMAGEQUALITY: // Add the Image Quality
                                    datalabel = (string)this.dbData.DataLabelFromType[Constants.IMAGEQUALITY];
                                    string str = Constants.IMAGEQUALITY_OK;
                                    if (imgprop_list[i].ImageQuality == (int)Constants.ImageQualityFilters.Dark) str = Constants.IMAGEQUALITY_DARK;
                                    else if (imgprop_list[i].ImageQuality == (int)Constants.ImageQualityFilters.Corrupted) str = Constants.IMAGEQUALITY_CORRUPTED;
                                    dataline.Add(datalabel, str);
                                    break;
                                case Constants.DELETEFLAG: // Add the Delete flag
                                    datalabel = (string)this.dbData.DataLabelFromType[Constants.DELETEFLAG];
                                    dataline.Add(datalabel, this.dbData.TemplateGetDefault(datalabel)); // Default as specified in the template file, which should be "false"
                                    break;
                                case Constants.NOTE:        // Find and then Add the Note or Fixed Choice
                                case Constants.FIXEDCHOICE:
                                    // Now initialize notes, counters, and fixed choices to the defaults
                                    foreach (string tkey in Notes_and_FixedChoicesList)
                                    {
                                        if (col_datalabel.Equals (tkey))
                                            dataline.Add(tkey, this.dbData.TemplateGetDefault(tkey) ); // Default as specified in the template file

                                    }
                                    break;
                                case Constants.FLAG:
                                    // Now initialize flags to the defaults
                                    foreach (string tkey in FlagsList)
                                    {
                                        if (col_datalabel.Equals(tkey))
                                            dataline.Add(tkey, this.dbData.TemplateGetDefault(tkey)); // Default as specified in the template file

                                    }
                                    break;
                                case Constants.COUNTER:
                                     foreach (string tkey in CounterList)
                                     {
                                        if (col_datalabel.Equals(tkey))
                                        {
                                            dataline.Add(tkey, this.dbData.TemplateGetDefault(tkey)); // Default as specified in the template file
                                            markerline.Add(tkey, "");        // TODO ASSUMES THAT MARKER LIST IS IN SAME ORDER AS COUNTERS. THIS MAY NOT BE CORRECT ONCE WE SWITCH ROWS, SO SHOULD DO THIS SEPARATELY
                                        }
                                     }
                                    break;

                                default:
                                    Debug.Print("Shouldn't ever reach here!");
                                    break;
                            }
                        }
                        dataline_list.Add(dataline);
                        if (markerline.Count > 0)
                            markerline_list.Add(markerline);
                        index = i;

                    }
                    this.dbData.InsertMultipleRows(Constants.TABLEDATA, dataline_list);
                    this.dbData.InsertMultipleRows(Constants.TABLEMARKERS, markerline_list);
                    j = j + interval - 1;
                    // Get the bitmap again to show it
                    if (imgprop_list[index].ImageQuality == (int)Constants.ImageQualityFilters.Corrupted)
                        bmap = corruptedbmp;
                    else
                        bmap = BitmapFrame.Create(new Uri(System.IO.Path.Combine(this.dbData.FolderPath, imgprop_list[index].Name)), BitmapCreateOptions.None, BitmapCacheOption.None);

                    // Show progress. Since its slow, we may as well do it every update
                    int progress2 = Convert.ToInt32(Convert.ToDouble(index) / Convert.ToDouble(count) * 100);
                    progressState.Message = String.Format("{0}/{1}: Adding {2}", index, count, imgprop_list[index].Name);
                    progressState.Bmap = bmap;
                    bgw.ReportProgress(progress2, progressState);
                }
                // this.dbData.AddNewRow(dataline);
                // this.dbData.AddNewRow(markerline, Constants.TABLEMARKERS);
            };
            bgw.ProgressChanged += (o, ea) =>
            {   // this gets called on the UI thread
                ProgressState progstate = (ProgressState)ea.UserState;
                Feedback (progressState.Bmap, ea.ProgressPercentage, progressState.Message);
                this.feedbackCtl.Visibility = System.Windows.Visibility.Visible;
            };
            bgw.RunWorkerCompleted += (o, ea) =>
            {

                // this.dbData.GetImagesAll(); // Now load up the data table
                // Get rid of the feedback panel, and show the main interface
                this.feedbackCtl.Visibility = Visibility.Collapsed;
                this.feedbackCtl.ShowImage = null;

                this.markableCanvas.Visibility = Visibility.Visible;

                // Finally warn the user if there are any ambiguous dates in terms of day/month or month/day order
                if (ambiguous_daymonth_order)
                {
                    DlgMessageBox dlgMB = new DlgMessageBox ();
                    dlgMB.MessageTitle = "Timelapse was unsure about the month / day order of your image's dates";
                    dlgMB.MessageProblem = "Timelapse is extracting the dates from your images. However, it cannot tell if the dates are in day/month order, or month/day order.";
                    dlgMB.MessageReason = "Image date formats can be ambiguous. For example, is 2015/03/05 March 5 or May 3?";
                    dlgMB.MessageSolution = "If Timelapse gets it wrong, you can correct the dates by choosing" + Environment.NewLine;
                    dlgMB.MessageSolution += "\u2022 Edit Menu -> Dates -> Swap Day and Month.";
                    dlgMB.MessageHint = "If you are unsure about the correct date, try the following." + Environment.NewLine;
                    dlgMB.MessageHint += "\u2022 If your camera prints the date on the image, check that." + Environment.NewLine;
                    dlgMB.MessageHint += "\u2022 Look at the images to see what season it is (e.g., winter vs. summer)." + Environment.NewLine;
                    dlgMB.MessageHint += "\u2022 Examine the creation date of the image file." + Environment.NewLine;
                    dlgMB.MessageHint += "\u2022 Check your own records.";
                    dlgMB.ButtonType = MessageBoxButton.OK;
                    dlgMB.IconType = MessageBoxImage.Information;
                    dlgMB.ShowDialog();
                }
                LoadComplete(true);
                // If we want to import old data from the ImageData.xml file, we can do it here...
                // Check to see if there is an ImageData.xml file in here. If there is, ask the user
                // if we want to load the data from that...
                if (File.Exists(System.IO.Path.Combine(this.FolderPath, Constants.XMLDATAFILENAME)))
                {
                    DlgImportImageDataXMLFile dlg = new DlgImportImageDataXMLFile();
                    dlg.Owner = this;
                    bool? result3 = dlg.ShowDialog();
                    if (result3 == true)
                    {
                        ImageDataXML.Read(System.IO.Path.Combine(this.FolderPath, Constants.XMLDATAFILENAME), dbData.templateTable, dbData);
                        SetImageFilterAndIndex(this.dbData.State_Row, this.dbData.State_Filter); // to regenerate the controls and markers for this image
                    }
                }
            };
            bgw.RunWorkerAsync();
            return true;
        }
        private void RescanDates()
        {
            DateTimeHandler dateTimeHandler = new DateTimeHandler();
            FileInfo fileInfo;

            // Collect the image properties for for the 2nd pass into a list...
            ImageProperties imgprop;
            List<ImageProperties> imgprop_list = new List<ImageProperties>();

            Dictionary<String, String> dataline = new Dictionary<String, String>();   // Populate the data for the image

            // This tuple list will hold the id, key and value that we will want to update in the database
            List<Tuple<int, string, string>> list_to_update_db = new List<Tuple<int, string, string>>();

            // This list will hold key / value pairs that will be bound to the datagrid feedback,
            // which is the way to make those pairs appear in the data grid during background worker progress updates
            ObservableCollection<MyFeedbackPair> MyFeedbackPairList = new ObservableCollection<MyFeedbackPair>();
            this.dgFeedback.ItemsSource = MyFeedbackPairList;

            // all the different formats used by cameras, including ambiguities in month/day vs day/month orders.
            DateTimeStyles styles;
            CultureInfo invariantCulture;
            invariantCulture = CultureInfo.CreateSpecificCulture("");
            styles = DateTimeStyles.None;

            var bgw = new BackgroundWorker() { WorkerReportsProgress = true };
            bgw.DoWork += (ow, ea) =>
            {   // this runs on the background thread; its written as an anonymous delegate
                // We need to invoke this to allow updates on the UI
                this.Dispatcher.Invoke(new Action(() =>
                {
                    ;
                    // First, change the UIprovide some feedback
                    bgw.ReportProgress(0, new FeedbackMessage("Pass 1: Examining all images...", "Checking if dates/time differ"));
                }));

                // Pass 1. Check to see what dates/times need updating.
                int count = dbData.dataTable.Rows.Count;
                int j = 1;
                for (int i = 0; i < count; i++)
                {
                    fileInfo = new FileInfo(System.IO.Path.Combine(dbData.FolderPath, dbData.dataTable.Rows[i][Constants.FILE].ToString()));
                    BitmapSource bmap = null;

                    imgprop = new ImageProperties();                            // We will store the various times here
                    imgprop.Name = dbData.dataTable.Rows[i][Constants.FILE].ToString();
                    imgprop.ID = Int32.Parse(dbData.dataTable.Rows[i][Constants.ID].ToString());
                    string message = "";
                    try
                    {
                        // Get the image (if its there), get the new dates/times, and add it to the list of images to be updated
                        // Note that if the image can't be created, we will just to the catch.
                        bmap = BitmapFrame.Create(new Uri(fileInfo.FullName), BitmapCreateOptions.None, BitmapCacheOption.None);

                        // First we try to see if  can get a valid and parsable metadata date and time
                        BitmapMetadata meta = (BitmapMetadata)bmap.Metadata;        // Get the data from the metadata
                        if (null != meta.DateTaken)
                        {
                           DateTime dtDate;
                           if (DateTime.TryParse(meta.DateTaken, invariantCulture, styles, out dtDate))
                           {
                               imgprop.FinalDate = DateTimeHandler.StandardDateString(dtDate);
                               imgprop.FinalTime = DateTimeHandler.StandardTimeString(dtDate);
                               message += " Using metadata timestamp";
                           }
                        }
                        else  // Fallback as no meta data: We have to use the file date
                        {
                            // For some reason, different versions of Windows treat creation time and modification time differently,
                            // giving inconsistent values. So I just check both and take the lesser of the two.
                            DateTime creationTime = File.GetCreationTime(fileInfo.FullName);
                            DateTime writeTime = File.GetLastWriteTime(fileInfo.FullName);
                            DateTime fileTime = (DateTime.Compare(creationTime, writeTime) < 0) ? creationTime : writeTime;
                            imgprop.FinalDate = DateTimeHandler.StandardDateString(fileTime);
                            imgprop.FinalTime = DateTimeHandler.StandardTimeString(fileTime);
                            message += " Using File timestamp";
                        }
                        if (imgprop.FinalDate.Equals(dbData.dataTable.Rows[i][Constants.DATE].ToString()))
                        {
                            message += ", same date";
                            imgprop.FinalDate = ""; // If its the same, we won't copy it
                        }
                        else
                            message += ", different date";
                        if (imgprop.FinalTime.Equals(dbData.dataTable.Rows[i][Constants.TIME].ToString()))
                        {
                            message += ", same time";
                            imgprop.FinalTime = ""; // If its the same, we won't copy it
                        }
                        else
                            message += ", different time";
                        imgprop_list.Add(imgprop);
                    }
                    catch // Image isn't there
                    {
                        message += " , skipping as cannot open image.";
                    }
                    j++;
                    bgw.ReportProgress(0, new FeedbackMessage(imgprop.Name, message));
                    if (i % 100 == 0) System.Threading.Thread.Sleep(25); // Put in a delay every now and then, as otherwise the UI won't update.
                }

                // Pass 2. Update each date as needed
                string msg = "";
                bgw.ReportProgress(0, new FeedbackMessage("Pass 2: For selected images", "Updating only when dates or times differ..."));
                for (int i = 0; i < imgprop_list.Count; i++)
                {
                    if ( ! (imgprop_list[i].FinalDate.Equals("")) && !(imgprop_list[i].FinalTime.Equals("")))
                    {
                        // Both date and time need updating
                        list_to_update_db.Add(new Tuple<int, string, string>(imgprop_list[i].ID, Constants.DATE, imgprop_list[i].FinalDate));
                        list_to_update_db.Add(new Tuple<int, string, string>(imgprop_list[i].ID, Constants.TIME, imgprop_list[i].FinalTime));
                        msg = "Date / Time updated to: " + imgprop_list[i].FinalDate + " " + imgprop_list[i].FinalTime;
                    }
                    else if ( !(imgprop_list[i].FinalDate.Equals ("")))
                    {
                        // Only date needs updating
                        list_to_update_db.Add(new Tuple<int, string, string>(imgprop_list[i].ID, Constants.DATE, imgprop_list[i].FinalDate));
                        msg = "Date updated to: " + imgprop_list[i].FinalDate;
                    }
                    else if ( !(imgprop_list[i].FinalTime.Equals ("")))
                    {
                        list_to_update_db.Add(new Tuple<int, string, string>(imgprop_list[i].ID, Constants.TIME, imgprop_list[i].FinalTime));
                        // dbData.RowSetValueFromID(Constants.TIME, imgprop_list[i].FinalTime, imgprop_list[i].ID); // OLD WAY: ONE ROW AT A TIME. Can DELETE THIS
                        msg = "Time updated to: " + imgprop_list[i].FinalTime;
                    }
                    else
                    {
                        msg = "Updating not required";
                    }
                    bgw.ReportProgress(0, new FeedbackMessage(imgprop_list[i].Name, msg));
                    if (i % 100 == 0) System.Threading.Thread.Sleep(25); // Put in a delay every now and then, as otherwise the UI won't update.
                }
                bgw.ReportProgress(0, new FeedbackMessage("Writing to database...", "Please wait"));
                System.Threading.Thread.Sleep(25);
                dbData.RowsUpdateByRowIdKeyVaue(list_to_update_db);  // Write the updates to the database
                bgw.ReportProgress(0, new FeedbackMessage("Done", "Done"));
            };
            bgw.ProgressChanged += (o, ea) =>
            {
                FeedbackMessage message = (FeedbackMessage)ea.UserState;
                MyFeedbackPairList.Add(new MyFeedbackPair { Image = message.ImageName, Message = message.Message });
                this.dgFeedback.ScrollIntoView(dgFeedback.Items[dgFeedback.Items.Count - 1]);
            };
            bgw.RunWorkerCompleted += (o, ea) =>
            {
                this.OkButton.IsEnabled = false;
                this.CancelButton.IsEnabled = true;
            };
            bgw.RunWorkerAsync();
        }