Search code examples
c#multithreadingtuplesblockingcollection

Cannot Add Tuple to BlockingCollection: Error CS1503 in C#


I'm working on a project that involves object detection with real-time video in C#. As part of the project, I use a BlockingCollection to manage a list of bounding boxes for detected objects, and the details for each bounding box are held in a tuple. However, I'm encountering an error CS1503 when trying to add new tuples to the collection. Here's how I defined the BlockingCollection:

BlockingCollection<Tuple<int, int, int, int, string, string, Scalar, DateTime>> ObjectDetectionBbox = new BlockingCollection<Tuple<int, int, int, int, string, string, Scalar, DateTime>>();

And here's the line where I'm trying to add a new tuple to the collection:

ObjectDetectionBbox.Add(Tuple.Create(
    topLeft.X,                        
    topLeft.Y,                        
    bottomRight.X - topLeft.X,        
    bottomRight.Y - topLeft.Y,        
    className,                        
    trackedName,                      
    classColor,                       
    bboxTimestamp                     
));

The error message is:

Error CS1503 Argument 1: cannot convert from 'System.Tuple<int, int, int, int, string, string, OpenCvSharp.Scalar, System.Tuple<System.DateTime>>' to 'System.Tuple<int, int, int, int, string, string, OpenCvSharp.Scalar, System.DateTime>'

The error message seems to suggest that my tuple has a Tuple as the last element, but as you can see in my code, the last element is a DateTime.

I've confirmed that bboxTimestamp is indeed a DateTime, and I'm not sure why this error is happening. enter image description here

enter image description here

Does anyone know why this might be happening and how I could fix it? Any help would be greatly appreciated.

HERE IS THE FULL CODE FOR REFERENCE:

public async Task Main(System.Windows.Controls.Image imageControl, CancellationToken token)
        {
            BlockingCollection<Tuple<int, int, int, int, string, string, Scalar, DateTime>> ObjectDetectionBbox = new BlockingCollection<Tuple<int, int, int, int, string, string, Scalar, DateTime>>();
            BlockingCollection<Tuple<Mat, DateTime>> frameBuffer = new BlockingCollection<Tuple<Mat, DateTime>>(); // Our new frame buffer

            string cameraUrl = "http://cam1.infolink.ru/mjpg/video.mjpg";
            //string cameraUrl = "http://hotel-seerose.dyndns.org:8001/cgi-bin/faststream.jpg?stream=full&fps=0";
            string URLreturnBbox = "http://localhost:8003/api/v1/camera/get_frame";  // Updated endpoint URL
            await initializeRunServer(cameraUrl);
            var idFaceDictionary = new ConcurrentDictionary<long, object>();
            var classNames = await GetClassNames();
            var colorList = InitColorList(classNames.Count);

            Task processFrameTask = Task.Run(() => ProcessTask(imageControl, token, URLreturnBbox, classNames, colorList,idFaceDictionary, ObjectDetectionBbox));
            

            // Create video source
            MJPEGStream stream = new MJPEGStream(cameraUrl);


            // New frame event handler
            stream.NewFrame += (sender, eventArgs) =>
            {
                // Convert AForge.NET's Bitmap to OpenCvSharp's Mat
                Bitmap bitmap = (Bitmap)eventArgs.Frame.Clone();
                Mat frame = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap);
                
                // Get bbox info from ObjectDetectionBbox, if any
                while (ObjectDetectionBbox.TryTake(out var bbox)) // Replace if with while to draw all available bounding boxes
                {
                    // Ensure we only proceed if there is a detection and a valid class name.
                    if (!string.IsNullOrEmpty(bbox.Item5))
                    {
                        if (bbox.Item5 == "person" && bbox.Item6 != null) // Display if person is detected
                        {
                            DrawPerson(
                                frame,
                                bbox.Item1,
                                bbox.Item2,
                                bbox.Item3,
                                bbox.Item4,
                                bbox.Item6,
                                bbox.Item7
                            );
                        }
                        else
                        {
                            Cv2.Rectangle(frame, new OpenCvSharp.Point(bbox.Item1, bbox.Item2), new OpenCvSharp.Point(bbox.Item1 + bbox.Item3, bbox.Item2 + bbox.Item4), bbox.Item7, 1);
                            var caption = bbox.Item5;
                            Cv2.PutText(frame, caption, new OpenCvSharp.Point(bbox.Item1, bbox.Item2), HersheyFonts.HersheyComplex, 1, bbox.Item7, 1);
                        }
                    }
                }

                // Process and display the frame
                Application.Current.Dispatcher.Invoke(() =>
                {
                    UpdateDisplay(frame, imageControl);
                });
            };

            // Start video capture
            stream.Start();

            // Wait until cancellation is requested
            while (!token.IsCancellationRequested)
            {
                await Task.Delay(20);
            }

            // Stop video capture
            stream.SignalToStop();
            stream.WaitForStop();
        }

        public async Task ProcessTask(System.Windows.Controls.Image imageControl, CancellationToken token, string URLreturnBbox, IDictionary<int, string> classNames, List<Scalar> colorList, ConcurrentDictionary<long, object> idFaceDictionary, BlockingCollection<Tuple<int, int, int, int, string, string, Scalar, DateTime>> ObjectDetectionBbox)
        {
            while (!token.IsCancellationRequested)
            {
                if (RunOTModel && ObjectTracking)
                {
                    await GetObjectDetectionBbox(URLreturnBbox, classNames, colorList, idFaceDictionary, ObjectDetectionBbox);
                }
            }
        }




        private async Task GetObjectDetectionBbox(string boundingBoxesUrl, IDictionary<int, string> classNames, List<Scalar> colorList, ConcurrentDictionary<long, object> idFaceDictionary, BlockingCollection<Tuple<int, int, int, int, string, string, Scalar, DateTime>> ObjectDetectionBbox)
        {
            var resultsList = await RunYolo(boundingBoxesUrl);

            // New dictionary to keep track of names already assigned
            var assignedNames = new ConcurrentDictionary<string, object>();

            foreach (var results in resultsList)
            {
                if (!results.ContainsKey("class_")) continue;

                var classIndex = int.Parse(results["class_"].ToString());
                var className = classNames[classIndex];
                var classColor = colorList[classIndex];
                var topLeft = new OpenCvSharp.Point((long)results["x"], (long)results["y"]);
                var bottomRight = new OpenCvSharp.Point((long)results["w"], (long)results["h"]);
                string trackedName = null;
                long trackId = 0; // initialize with default value
                bool hasTrackId = results.ContainsKey("track_id");

                DateTime bboxTimestamp = DateTime.Parse(results["timestamp"].ToString());
                


                if (hasTrackId)
                {
                    trackId = (long)results["track_id"];
                }
                if (OTFaceRecognition && hasTrackId && className == "person" && !idFaceDictionary.ContainsKey(trackId))
                {
                    if (results.ContainsKey("result") && results["result"] != null)
                    {
                        var resultStr = results["result"].ToString();
                        // Check if this name has not been assigned already
                        if (!string.IsNullOrEmpty(resultStr) && !assignedNames.ContainsKey(resultStr))
                        {
                            // Remove any existing entries with the same value
                            foreach (var kvp in idFaceDictionary.Where(kvp => kvp.Value.Equals(resultStr)).ToList())
                            {
                                idFaceDictionary.TryRemove(kvp.Key, out _);
                            }
                            // Add the new entry
                            idFaceDictionary.TryAdd(trackId, resultStr);
                            // Add this name to the assigned names
                            assignedNames[resultStr] = new object();
                        }
                    }
                }
                if (OTFaceRecognition && hasTrackId)
                {
                    trackedName = idFaceDictionary.ContainsKey(trackId)
                        ? $"{idFaceDictionary[trackId]} - ID {results["track_id"]}"
                        : null;
                }

                ObjectDetectionBbox.Add(Tuple.Create(
                    topLeft.X,                        // int
                    topLeft.Y,                        // int
                    bottomRight.X - topLeft.X,        // int
                    bottomRight.Y - topLeft.Y,        // int
                    className,                        // string
                    trackedName,                      // string
                    classColor,                       // Scalar
                    bboxTimestamp                     // DateTime
                    ));
            }
        }

Solution

  • Tuple.Create<T1,T2,T3,T4,T5,T6,T7,T8>(T1, T2, T3, T4, T5, T6, T7, T8) has the following return type:

    Tuple<T1,T2,T3,T4,T5,T6,T7,Tuple<T8>>
    

    So the last element is not the the type but tuple of single hence the error. Minimal reproducible example would be something like the following:

    Tuple<int, int, int, int, string, string, double, DateTime> tup 
      = Tuple.Create(1, 1, 1, 1, "", "", 2.0, DateTime.UtcNow);
    

    You can use the ctor for Tuple directly:

    Tuple<int, int, int, int, string, string, double, DateTime> tup 
        = new Tuple<int, int, int, int, string, string, double, DateTime>(1, 1, 1, 1, "", "", 2.0, DateTime.UtcNow);
    

    But in general I would recommend to avoid using tuples for such "big" number of items - just introduce a class/struct/record.