private void btn_adddefaults_Click(object sender, EventArgs e)
 {
     using var Trace = new Trace();        //This c# 8.0 using feature will auto dispose when the function is done.
     this.TempObjectManager.AddDefaults(); // = new ClsRelevantObjectManager(AppSettings.Settings.ObjectPriority, this.ObjectManager.TypeName, this.ObjectManager.Camera);
     this.ro = null;
     Global_GUI.UpdateFOLV(FOLV_RelevantObjects, this.TempObjectManager.ObjectList, UseSelected: true, SelectObject: this.ro, FullRefresh: true, ForcedSelection: true);
 }
        public ClsRelevantObject Delete(ClsRelevantObject ro, out int NewIDX)
        {
            ClsRelevantObject ret = ro;

            NewIDX = 0;

            if (ro == null)
            {
                return(ro);
            }

            ClsRelevantObject rofound = this.Get(ro, false, out int FoundIDX, true);

            if (!rofound.IsNull())
            {
                this.ObjectList.RemoveAt(FoundIDX);
                NewIDX = FoundIDX - 1;

                this.Update(false);

                if (NewIDX > -1)
                {
                    ret = this.ObjectList[NewIDX];
                }
            }

            return(ret);
        }
        private void SelectionChanged()
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.
            try
            {
                if (this.FOLV_RelevantObjects.SelectedObjects != null && this.FOLV_RelevantObjects.SelectedObjects.Count > 0)
                {
                    ////save the last one just in case
                    this.SaveRO();

                    //set current selected object
                    this.ro = (ClsRelevantObject)this.FOLV_RelevantObjects.SelectedObjects[0];

                    //enable toolstrip buttons
                    toolStripButtonDelete.Enabled = true;
                    toolStripButtonDown.Enabled   = true;
                    toolStripButtonUp.Enabled     = true;
                }
                else
                {
                    toolStripButtonDelete.Enabled = false;
                    toolStripButtonDown.Enabled   = false;
                    toolStripButtonUp.Enabled     = false;
                    this.ro = null;
                }
            }
            catch (Exception ex)
            {
                AITOOL.Log($"Error: {ex.Msg()}");
            }

            LoadRO();
        }
        public ClsRelevantObject MoveDown(ClsRelevantObject ro, out int NewIDX)
        {
            ClsRelevantObject ret = ro;

            NewIDX = 0;

            if (ro == null)
            {
                return(ro);
            }

            ClsRelevantObject rofound = this.Get(ro, false, out int FoundIDX, true);

            if (!rofound.IsNull())
            {
                NewIDX = FoundIDX + 1;

                if (NewIDX < this.ObjectList.Count - 1)
                {
                    this.ObjectList.Move(FoundIDX, NewIDX);
                    this.Update(false);
                    ret = this.ObjectList[NewIDX];
                }
            }

            return(ret);
        }
        public bool TryAdd(ClsRelevantObject ro, bool Enable, out int AddedIDX)
        {
            bool ret = false;

            AddedIDX = -1;

            if (ro.IsNull())
            {
                return(false);
            }

            ClsRelevantObject rofound = this.Get(ro, false, out int FoundIDX, true);

            if (rofound.IsNull())
            {
                ro.Priority = this.ObjectList.Count + 1;
                ro.Enabled  = Enable;

                if (ro.Enabled)
                {
                    this.EnabledCount++;
                }

                ro.Update();
                this.ObjectList.Add(ro);
                AddedIDX = this.ObjectList.Count - 1;
                ret      = true;
            }

            return(ret);
        }
        public ClsRelevantObject Get(ClsRelevantObject roobj, bool AllowEverything, out int FoundIDX, bool ExactMatchOnly, List <ClsRelevantObject> ObjList = null)
        {
            if (ObjList.IsNull())
            {
                ObjList = this.ObjectList;
            }

            //look for exact hashcode first
            FoundIDX = ObjList.IndexOf(roobj);

            if (FoundIDX > -1)
            {
                return(ObjList[FoundIDX]);
            }

            //search for the last hashcode in case the object has changed
            for (int i = 0; i < ObjList.Count; i++)
            {
                if (ObjList[i].LastHashCode != 0 && ObjList[i].LastHashCode == roobj.LastHashCode && !roobj.Name.EqualsIgnoreCase("new object"))
                {
                    FoundIDX = i;
                    return(ObjList[i]);
                }
            }

            //fall back to name only search
            if (!ExactMatchOnly)
            {
                return(this.Get(roobj.Name, AllowEverything, out FoundIDX, ObjList));
            }

            return(null);
        }
        private void toolStripButtonDown_Click(object sender, EventArgs e)
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            if (this.ro == null)
            {
                return;
            }

            int idx = this.ObjectList.IndexOf(this.ro);

            if (idx > -1 && idx < this.ObjectList.Count - 1)
            {
                this.ObjectList.Move(idx, idx + 1);

                this.ro = this.ObjectList[idx + 1];

                for (int i = 0; i < this.ObjectList.Count; i++)
                {
                    this.ObjectList[i].Priority = i + 1;
                }

                Global_GUI.UpdateFOLV(FOLV_RelevantObjects, this.ObjectList, UseSelected: true, SelectObject: this.ro, FullRefresh: true, ForcedSelection: true);
            }
        }
        private void toolStripButtonDelete_Click(object sender, EventArgs e)
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            this.ro = this.TempObjectManager.Delete(ro, out int NewIDX);

            Global_GUI.UpdateFOLV(FOLV_RelevantObjects, this.TempObjectManager.ObjectList, UseSelected: true, SelectObject: this.ro, FullRefresh: true, ForcedSelection: true);
        }
        public void Init(Camera cam = null)
        {
            if (!cam.IsNull())
            {
                this.cam = cam;
            }

            if (this.cam.IsNull() && this.CameraName.IsNotEmpty())
            {
                this.cam = AITOOL.GetCamera(this.CameraName);
            }

            if (!this.cam.IsNull())
            {
                this.CameraName = this.cam.Name;
            }

            //dont initialize until we have a list of cameras available
            if (!this.Initialized || !this.ObjectDict.IsNull() && !AppSettings.Settings.CameraList.IsNull() && AppSettings.Settings.CameraList.Count > 0)
            {
                lock (ROLockObject)
                {
                    //migrate from the dictionary to the list - dictionary no longer used
                    if (!this.ObjectDict.IsNull())
                    {
                        if (this.ObjectDict.Count > 0)
                        {
                            ObjectList.Clear();
                            foreach (Object item in ObjectDict.Values)
                            {
                                if (item is DictionaryEntry)
                                {
                                    ClsRelevantObject ro = (ClsRelevantObject)((DictionaryEntry)item).Value;
                                    ObjectList.Add(ro.CloneJson());
                                }
                                else if (item is ClsRelevantObject)
                                {
                                    ClsRelevantObject ro = (ClsRelevantObject)item;
                                    ObjectList.Add(ro.CloneJson());
                                }
                                else
                                {
                                    AITOOL.Log($"Warn: Old object is {item.GetType().FullName}??");
                                }
                            }
                        }

                        this.ObjectDict = null;
                    }

                    //Add default settings
                    this.Initialized = true;

                    Update();
                }
            }
        }
Пример #10
0
        private void toolStripButtonAdd_Click(object sender, EventArgs e)
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.
            SaveRO();

            ClsRelevantObject ro = new ClsRelevantObject("NEW OBJECT");


            this.ro          = ro;
            this.ro.Priority = this.ObjectList.Count + 1;
            this.ObjectList.Add(ro);
            Global_GUI.UpdateFOLV(FOLV_RelevantObjects, this.ObjectList, UseSelected: true, SelectObject: this.ro, FullRefresh: true, ForcedSelection: true);
        }
        private void toolStripButtonAdd_Click(object sender, EventArgs e)
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.
            SaveRO();

            ClsRelevantObject ro = new ClsRelevantObject("NEW OBJECT");

            this.ro = ro;

            if (this.TempObjectManager.TryAdd(ro, true, out int NewIDX))
            {
                Global_GUI.UpdateFOLV(FOLV_RelevantObjects, this.TempObjectManager.ObjectList, UseSelected: true, SelectObject: this.ro, FullRefresh: true, ForcedSelection: true);
            }
        }
Пример #12
0
        public override string ToString()
        {
            string ret = "";

            foreach (DictionaryEntry entry in this.ObjectDict)
            {
                ClsRelevantObject ro = (ClsRelevantObject)entry.Value;
                if (ro.Enabled)
                {
                    ret += ro.ToString() + ", ";
                }
            }
            return(ret.Trim(" ,".ToCharArray()));
        }
Пример #13
0
        public void FromString(string Objects)
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            try
            {
                //take anything before the first semicolon:
                if (Objects.Contains(";"))
                {
                    Objects = Objects.GetWord("", ";");
                }

                this.EnabledCount = 0;
                int order = 0;

                List <string> lst    = Objects.SplitStr(",");
                List <string> deflst = AppSettings.Settings.ObjectPriority.ToLower().SplitStr(",");

                foreach (var obj in lst)
                {
                    ClsRelevantObject ro = new ClsRelevantObject(obj);

                    if (!ObjectDict.Contains(ro.Name.ToLower()))
                    {
                        if (ro.Enabled)
                        {
                            this.EnabledCount++;
                        }

                        order = deflst.IndexOf(ro.Name.ToLower());

                        if (order > -1)
                        {
                            ro.Priority = order + 1;
                        }

                        ObjectDict.Add(ro.Name.ToLower(), ro);
                    }
                }

                AddDefaults();
            }
            catch (Exception ex)
            {
                AITOOL.Log("Error: " + ex.Msg());
            }
        }
Пример #14
0
        public bool TryDelete(ClsRelevantObject ro)
        {
            bool ret = false;

            if (ro.IsNull())
            {
                return(false);
            }

            if (ObjectDict.Contains(ro.Name.ToLower()))
            {
                ObjectDict.Remove(ro.Name.ToLower());
                ret = true;
            }

            return(ret);
        }
Пример #15
0
        public void AddDefaults()
        {
            //Make sure the default objects are always in the list

            List <string> deflst          = AppSettings.Settings.ObjectPriority.SplitStr(",");
            bool          AlreadyHasItems = ObjectDict.Count > 0;

            for (int i = 0; i < deflst.Count; i++)
            {
                ClsRelevantObject ro = new ClsRelevantObject(deflst[i]);

                if (!ObjectDict.Contains(ro.Name.ToLower()))
                {
                    //if no items are currently in the dictionary, assume we want to ENABLE all the objects
                    //  Otherwise, Disable objects so that existing lists from old versions dont suddenly have everything enabled that shouldnt be
                    if (!AlreadyHasItems)
                    {
                        ro.Enabled = true;
                    }
                    else
                    {
                        ro.Enabled = false;
                    }

                    if (ro.Enabled)
                    {
                        this.EnabledCount++;
                    }

                    ro.Priority = i + 1;

                    ObjectDict.Add(ro.Name.ToLower(), ro);
                }
                else
                {
                    if (!AlreadyHasItems)
                    {
                        ro.Priority = i + 1;
                    }
                }
            }
        }
        public void Update(bool ResetIfNeeded = true)
        {
            //sort
            //this.ObjectList = this.ObjectList.OrderByDescending(ro => ro.Enabled).ThenBy(ro => ro.Priority).ThenBy(ro => ro.CreatedTime).ThenBy(ro => ro.Name).ToList();

            if (this.cam.IsNull())
            {
                this.Init();
            }

            if (ResetIfNeeded && this.ObjectList.Count == 0 && !this.CameraName.EqualsIgnoreCase("default"))
            {
                this.Reset();
            }

            bool restrict = !this.cam.IsNull() && !this.cam.DefaultTriggeringObjects.IsNull() && this.cam.DefaultTriggeringObjects.TypeName != this.TypeName;

            //make sure no priority is in order and the minimum is not less than the main cameras list
            for (int i = 0; i < this.ObjectList.Count; i++)
            {
                if (restrict)
                {
                    ClsRelevantObject ro = this.Get(this.ObjectList[i].Name, false, out int FoundIDX, UseMainCamList: true);
                    if (!ro.IsNull())
                    {
                        this.ObjectList[i].Update(ro.Threshold_lower, ro.Threshold_upper, ro.IgnoreDynamicMask, ro.IgnoreImageMask);
                    }
                    else
                    {
                        this.ObjectList[i].Update();
                    }
                }
                else
                {
                    this.ObjectList[i].Update();
                }
                this.ObjectList[i].Priority = i + 1;
            }
        }
        private void FormatRow(object Sender, BrightIdeasSoftware.FormatRowEventArgs e)
        {
            try
            {
                ClsRelevantObject ro = (ClsRelevantObject)e.Model;

                // If SPI IsNot Nothing Then
                if (ro.Enabled && ro.Trigger)
                {
                    e.Item.ForeColor = Color.Green;
                }

                else if (ro.Enabled && !ro.Trigger)
                {
                    e.Item.ForeColor = Color.DarkOrange;
                }

                else if (!ro.Enabled)
                {
                    e.Item.ForeColor = Color.Gray;
                }
            }
            catch (Exception) { }
        }
Пример #18
0
        public List <ClsRelevantObject> ToList()
        {
            this.AddDefaults();

            List <ClsRelevantObject> ret = new List <ClsRelevantObject>();

            this.EnabledCount = 0;
            int Order = 0;

            foreach (DictionaryEntry entry in this.ObjectDict)
            {
                ClsRelevantObject ro = (ClsRelevantObject)entry.Value;

                if (ro.Enabled)
                {
                    this.EnabledCount++;
                    Order++;
                    ro.Priority = Order;
                    ret.Add(ro);
                }
            }
            //force disabled items to be lower priority
            foreach (DictionaryEntry entry in this.ObjectDict)
            {
                ClsRelevantObject ro = (ClsRelevantObject)entry.Value;

                if (!ro.Enabled)
                {
                    Order++;
                    ro.Priority = Order;
                    ret.Add(ro);
                }
            }

            return(ret);
        }
Пример #19
0
        public bool TryAdd(ClsRelevantObject ro, bool Enable)
        {
            bool ret = false;

            if (ro.IsNull())
            {
                return(false);
            }

            if (!ObjectDict.Contains(ro.Name.ToLower()))
            {
                ro.Priority = ObjectDict.Count + 1;
                ro.Enabled  = Enable;

                if (ro.Enabled)
                {
                    this.EnabledCount++;
                }
                ObjectDict.Add(ro.Name.ToLower(), ro);
                ret = true;
            }

            return(ret);
        }
        public ResultType IsRelevant(List <ClsPrediction> preds, bool IsNew, out bool IgnoreMask, string DbgDetail = "")
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            ResultType ret = ResultType.UnwantedObject;

            IgnoreMask = false;

            this.Init();
            if (!this.Initialized)
            {
                return(ret);
            }


            //if nothing is 'enabled' assume everything should be let through to be on the safe side  (As if they passed an empty list)
            if (this.ObjectList.Count == 0 || this.EnabledCount == 0)  //assume if no list is provided to always return relevant
            {
                return(ResultType.Relevant);
            }

            //if fred is found, the whole prediction will be ignored
            //triggering_objects = person, car, -FRED
            //found objects = person, fred

            if (preds != null && preds.Count > 0)
            {
                //find at least one thing in the triggered objects list in order to send
                string notrelevant = "";
                string relevant    = "";
                string ignored     = "";
                string notenabled  = "";
                string nottime     = "";
                string nothreshold = "";
                bool   ignore      = false;

                foreach (ClsPrediction pred in preds)
                {
                    string label = pred.Label;


                    if (pred.Result == ResultType.Relevant || IsNew)
                    {
                        ClsRelevantObject ro = this.Get(label, AllowEverything: true, out int FoundIDX);

                        if (!ro.IsNull())
                        {
                            if (ro.Enabled)
                            {
                                if (Global.IsTimeBetween(DateTime.Now, ro.ActiveTimeRange))
                                {
                                    //assume if confidence is 0 it has not been set yet (dynamic masking routine, etc)
                                    if (pred.Confidence == 0 || pred.Confidence.Round() >= ro.Threshold_lower && pred.Confidence.Round() <= ro.Threshold_upper)
                                    {
                                        ro.LastHitTime = DateTime.Now;
                                        ro.Hits++;
                                        if (!ro.Trigger)
                                        {
                                            ignore = true;
                                            if (!ignored.Contains(label))
                                            {
                                                ignored += label + ",";
                                            }
                                        }
                                        else
                                        {
                                            ret        = ResultType.Relevant;
                                            IgnoreMask = ro.IgnoreMask;
                                            if (!relevant.Contains(label))
                                            {
                                                relevant += label + ",";
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (!nothreshold.Contains(label))
                                        {
                                            nothreshold += label + $" ({pred.Confidence.Round()}%),";
                                        }
                                    }
                                }
                                else
                                {
                                    if (!nottime.Contains(label))
                                    {
                                        nottime += label + ",";
                                    }
                                }
                            }
                            else
                            {
                                if (!notenabled.Contains(label))
                                {
                                    notenabled += label + ",";
                                }
                            }
                        }
                        else
                        {
                            if (!notrelevant.Contains(label))
                            {
                                notrelevant += label + ",";
                            }
                        }
                    }
                    else
                    {
                        if (!notrelevant.Contains(label))
                        {
                            notrelevant += label + ",";
                        }
                    }
                }

                //Add to the main list
                if (this.cam == null)
                {
                    this.cam = AITOOL.GetCamera(this.Camera);
                }

                if (this.defaultcam == null)
                {
                    this.defaultcam = AITOOL.GetCamera("Default", true);
                }

                //always try to add the current prediction to the list (disabled) to give them the option of enabling later
                foreach (ClsPrediction pred in preds)
                {
                    this.TryAdd(pred.Label, false, out int AddedIDX);


                    //add it to the Current camera list (disabled)
                    this.cam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX2);

                    //add it to the default camera list (disabled)
                    if (!this.defaultcam.IsNull())
                    {
                        this.defaultcam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX3);
                    }
                }


                if (ignore)
                {
                    ret = ResultType.IgnoredObject;
                }

                if (!DbgDetail.IsEmpty())
                {
                    DbgDetail = $" ({DbgDetail})";
                }

                if (relevant.IsEmpty())
                {
                    relevant = "(NONE)";
                }

                if (notrelevant.IsEmpty())
                {
                    notrelevant = "(NONE)";
                }

                if (ignored.IsEmpty())
                {
                    ignored = "(NONE)";
                }

                if (notenabled.IsEmpty())
                {
                    notenabled = "(NONE)";
                }

                if (nottime.IsEmpty())
                {
                    nottime = "(NONE)";
                }

                if (nothreshold.IsEmpty())
                {
                    nothreshold = "(NONE)";
                }

                string maskignore = "";
                if (IgnoreMask)
                {
                    maskignore = " (Mask will be ignored)";
                }

                if (ret != ResultType.Relevant)
                {
                    AITOOL.Log($"Trace: RelevantObjectManager: Skipping '{this.TypeName}{DbgDetail}' because objects were not defined to trigger, or were set to ignore: Relevant='{relevant.Trim(", ".ToCharArray())}', Irrelevant='{notrelevant.Trim(", ".ToCharArray())}', Caused ignore='{ignored.Trim(", ".ToCharArray())}', Not Enabled={notenabled.Trim(" ,".ToCharArray())}, Not Time={nottime.Trim(" ,".ToCharArray())}, No Threshold Match={nothreshold.Trim(" ,".ToCharArray())}  All Triggering Objects='{this.ToString()}', {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}");
                }
                else
                {
                    AITOOL.Log($"Trace: RelevantObjectManager: Object is valid for '{this.TypeName}{DbgDetail}' because object(s) '{relevant.Trim(", ".ToCharArray())}' were in trigger objects list '{this.ToString()}',{maskignore} Enabled={this.EnabledCount} of {this.ObjectList.Count}");
                }
            }

            return(ret);
        }
        public ResultType IsRelevant(List <ClsPrediction> preds, bool IsNew, out bool IgnoreImageMask, out bool IgnoreDynamicMask, string DbgDetail = "")
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            ResultType ret = ResultType.UnwantedObject;

            IgnoreImageMask   = false;
            IgnoreDynamicMask = false;

            this.Init();
            if (!this.Initialized)
            {
                return(ret);
            }


            //if nothing is 'enabled' assume everything should be let through to be on the safe side  (As if they passed an empty list)
            if (this.ObjectList.Count == 0 || this.EnabledCount == 0)  //assume if no list is provided to always return relevant
            {
                AITOOL.Log($"Debug: RelevantObjectManager: Allowing '{this.TypeName}{DbgDetail}' because no relevant objects were enabled. {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}");
                return(ResultType.Relevant);
            }

            //if fred is found, the whole prediction will be ignored
            //triggering_objects = person, car, -FRED
            //found objects = person, fred

            if (preds != null && preds.Count > 0)
            {
                //find at least one thing in the triggered objects list in order to send
                string notrelevant      = "";
                string relevant         = "";
                string ignored          = "";
                string unknownobject    = "";
                string notenabled       = "";
                string nottime          = "";
                string nothreshold      = "";
                string percentsizewrong = "";
                bool   ignore           = false;

                foreach (ClsPrediction pred in preds)
                {
                    string label = pred.Label;


                    if (pred.Result == ResultType.Relevant || IsNew)
                    {
                        ClsRelevantObject ro = this.Get(label, AllowEverything: true, out int FoundIDX);

                        if (!ro.IsNull())
                        {
                            //Add the PercentOfImage size stats in any case:
                            if (pred.Confidence == 0 || pred.Confidence.Round() >= ro.Threshold_lower)
                            {
                                ro.PercentSizeStats.AddToCalc(pred.PercentOfImage);
                                ro.HeightStats.AddToCalc(pred.RectHeight);
                                ro.WidthStats.AddToCalc(pred.RectWidth);
                            }

                            if (ro.Enabled)
                            {
                                if (Global.IsTimeBetween(DateTime.Now, ro.ActiveTimeRange))
                                {
                                    //assume if confidence is 0 it has not been set yet (dynamic masking routine, etc)
                                    if (pred.Confidence == 0 || pred.Confidence.Round() >= ro.Threshold_lower && pred.Confidence.Round() <= ro.Threshold_upper)
                                    {
                                        if (pred.PercentOfImage.Round() >= ro.PredSizeMinPercentOfImage && pred.PercentOfImage.Round() <= ro.PredSizeMaxPercentOfImage)
                                        {
                                            ro.LastHitTime = DateTime.Now;
                                            ro.Hits++;
                                            if (!ro.Trigger)
                                            {
                                                ignore = true;
                                                if (!ignored.Contains(label))
                                                {
                                                    ignored += label + ",";
                                                }
                                            }
                                            else
                                            {
                                                ret = ResultType.Relevant;
                                                pred.ObjectResult = ResultType.Relevant;
                                                IgnoreImageMask   = ro.IgnoreImageMask;
                                                if (ro.IgnoreDynamicMask.HasValue)
                                                {
                                                    IgnoreDynamicMask = ro.IgnoreDynamicMask.Value;
                                                }
                                                if (!relevant.Contains(label))
                                                {
                                                    relevant += label + ",";
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (pred.Confidence.Round() < ro.Threshold_lower)
                                            {
                                                pred.ObjectResult = ResultType.TooSmallPercent;
                                            }
                                            else if (pred.Confidence.Round() > ro.Threshold_upper)
                                            {
                                                pred.ObjectResult = ResultType.TooLargePercent;
                                            }

                                            if (!percentsizewrong.Contains(label))
                                            {
                                                percentsizewrong += label + $" ({pred.PercentOfImage.Round()}%),";
                                            }
                                        }
                                    }
                                    else
                                    {
                                        pred.ObjectResult = ResultType.NoConfidence;
                                        if (!nothreshold.Contains(label))
                                        {
                                            nothreshold += label + $" ({pred.Confidence.Round()}%),";
                                        }
                                    }
                                }
                                else
                                {
                                    pred.ObjectResult = ResultType.NotInTimeRange;
                                    if (!nottime.Contains(label))
                                    {
                                        nottime += label + ",";
                                    }
                                }
                            }
                            else
                            {
                                pred.ObjectResult = ResultType.NotEnabled;
                                if (!notenabled.Contains(label))
                                {
                                    notenabled += label + ",";
                                }
                            }
                        }
                        else
                        {
                            pred.ObjectResult = ResultType.UnknownObject;
                            if (!unknownobject.Contains(label))
                            {
                                unknownobject += label + ",";
                            }
                        }
                    }
                    else
                    {
                        pred.ObjectResult = pred.Result;
                        if (!notrelevant.Contains(label))
                        {
                            notrelevant += label + ",";
                        }
                    }
                }

                //Add to the main list
                if (this.cam == null)
                {
                    this.cam = AITOOL.GetCamera(this.CameraName);
                }

                if (this.defaultcam == null)
                {
                    this.defaultcam = AITOOL.GetCamera("Default", true);
                }

                //always try to add the current prediction to the list (disabled) to give them the option of enabling later
                foreach (ClsPrediction pred in preds)
                {
                    this.TryAdd(pred.Label, false, out int AddedIDX);


                    //add it to the Current camera list (disabled)
                    this.cam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX2);

                    //add it to the default camera list (disabled)
                    if (!this.defaultcam.IsNull())
                    {
                        this.defaultcam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX3);
                    }
                }


                if (ignore)
                {
                    ret = ResultType.IgnoredObject;
                }

                if (!DbgDetail.IsEmpty())
                {
                    DbgDetail = $" ({DbgDetail})";
                }

                if (!relevant.IsEmpty())
                {
                    relevant = $"Relevant='{relevant.Trim(", ".ToCharArray())}',";
                }

                if (!notrelevant.IsEmpty())
                {
                    notrelevant = $"Irrelevant='{notrelevant.Trim(", ".ToCharArray())}',";
                }

                if (!ignored.IsEmpty())
                {
                    ignored = $"Caused ignore='{ignored.Trim(", ".ToCharArray())}',";
                }

                if (!notenabled.IsEmpty())
                {
                    notenabled = $"Not Enabled={notenabled.Trim(" ,".ToCharArray())},";
                }

                if (!nottime.IsEmpty())
                {
                    nottime = $"Not Time={nottime.Trim(" ,".ToCharArray())},";
                }

                if (!nothreshold.IsEmpty())
                {
                    nothreshold = $"No Threshold Match='{nothreshold.Trim(" ,".ToCharArray())}',";
                }

                if (!percentsizewrong.IsEmpty())
                {
                    percentsizewrong = $"Percent Size wrong='{percentsizewrong.Trim(" ,".ToCharArray())}',";
                }

                if (!unknownobject.IsEmpty())
                {
                    unknownobject = $"Unknown Object='{unknownobject.Trim(" ,".ToCharArray())}',";
                }
                ;

                string maskignore = "";
                if (IgnoreImageMask)
                {
                    maskignore = " (Mask will be ignored)";
                }

                if (ret != ResultType.Relevant)
                {
                    AITOOL.Log($"Debug: RelevantObjectManager: Skipping '{this.TypeName}{DbgDetail}' because: {notrelevant}{ignored}{notenabled}{nottime}{nothreshold}{percentsizewrong}{unknownobject}{relevant}. {maskignore}, {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}, IsNew={IsNew}");
                }
                else
                {
                    AITOOL.Log($"Debug: RelevantObjectManager: Object is valid for '{this.TypeName}{DbgDetail}' because:  {relevant}{notrelevant}{ignored}{notenabled}{nottime}{nothreshold}{percentsizewrong}{unknownobject}. {maskignore}, {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}, IsNew={IsNew}");
                }
            }
            else
            {
                AITOOL.Log($"Debug: RelevantObjectManager: Skipping '{this.TypeName}{DbgDetail}' because there were no PREDICTIONS.");
            }

            return(ret);
        }
        public List <ClsRelevantObject> FromString(string Objects, bool Clear, bool ExactMatchonly)
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            List <ClsRelevantObject> ret = new List <ClsRelevantObject>();

            this.Init();
            if (!this.Initialized)
            {
                return(ret);
            }


            try
            {
                if (Clear)
                {
                    this.ObjectList.Clear();
                    this.EnabledCount = 0;
                }
                //take anything before the first semicolon:
                if (Objects.Contains(";"))
                {
                    Objects = Objects.GetWord("", ";");
                }

                bool AlreadyHasItems = ObjectList.Count > 0;

                List <string>            lst = Objects.SplitStr(",");
                List <ClsRelevantObject> DefaultObjectList = new List <ClsRelevantObject>();

                if (!this.Camera.EqualsIgnoreCase("default"))
                {
                    DefaultObjectList = this.GetDefaultObjectList(false);
                }

                foreach (var obj in lst)
                {
                    ClsRelevantObject ro = new ClsRelevantObject(obj);

                    ClsRelevantObject rofound = this.Get(ro, false, out int FoundIDX, ExactMatchonly, ret);

                    if (rofound.IsNull())
                    {
                        //Only add items as enabled if we started out from an empty list
                        if (AlreadyHasItems)
                        {
                            ro.Enabled = false;
                        }

                        if (ro.Enabled)
                        {
                            this.EnabledCount++;
                        }

                        //set the order if found in the default list
                        ClsRelevantObject defRo = this.Get(ro, false, out int DefFoundIDX, false, DefaultObjectList);

                        if (DefFoundIDX > -1)
                        {
                            ro.Priority = DefFoundIDX + 1;
                        }
                        else
                        {
                            ro.Priority = ret.Count + 1;
                        }

                        ret.Add(ro);
                    }
                }
            }
            catch (Exception ex)
            {
                AITOOL.Log("Error: " + ex.Msg());
            }

            return(ret);
        }
        public List <ClsRelevantObject> FromList(List <ClsRelevantObject> InList, bool Clear, bool ExactMatchOnly)
        {
            List <ClsRelevantObject> ret = new List <ClsRelevantObject>();

            if (InList.Count == 0)
            {
                return(ret);
            }

            this.Init();
            if (!this.Initialized)
            {
                return(ret);
            }

            if (Clear)
            {
                this.ObjectList.Clear();
                this.EnabledCount = 0;
            }

            ret.AddRange(this.ObjectList);

            int order = ret.Count - 1;

            bool AlreadyHasItems = ret.Count > 0;
            int  dupes           = 0;

            foreach (var ro in InList)
            {
                ClsRelevantObject rofound = this.Get(ro, false, out int FoundIDX, ExactMatchOnly, ret);

                if (rofound.IsNull())
                {
                    //Only add items as enabled if we started out from an empty list
                    if (AlreadyHasItems)
                    {
                        ro.Enabled = false;
                    }

                    order++;
                    this.EnabledCount++;
                    ro.Priority = order;
                    ro.Update();
                    ret.Add(ro.CloneJson());
                }
                else
                {
                    dupes++;
                }
            }

            ////force disabled items to be lower priority
            //foreach (var ro in InList)
            //{
            //    ClsRelevantObject rofound = this.Get(ro, false, out int FoundIDX);
            //    if (rofound.IsNull())
            //    {
            //        //Only add items as enabled if we started out from an empty list
            //        if (!AlreadyHasItems)
            //            ro.Enabled = true;
            //        else
            //            ro.Enabled = false;

            //        if (!ro.Enabled)
            //        {
            //            order++;
            //            ro.Priority = order;
            //            ro.Update();
            //            ret.Add(ro);
            //        }
            //    }
            //}

            return(ret);
        }