changed prints to logging in utils/datasets
diff --git a/utils/datasets.py b/utils/datasets.py
index 841879a5cf8..5f1ff5f1a7f 100755
--- a/utils/datasets.py
+++ b/utils/datasets.py
@@ -1,6 +1,7 @@
 # Dataset utils and dataloaders
 
 import glob
+import logging
 import math
 import os
 import random
@@ -21,6 +22,8 @@
 from utils.general import xyxy2xywh, xywh2xyxy
 from utils.torch_utils import torch_distributed_zero_first
 
+logger = logging.getLogger(__name__)
+
 # Parameters
 help_url = 'https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data'
 img_formats = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.tiff', '.dng']
@@ -165,14 +168,14 @@ def __next__(self):
                     ret_val, img0 = self.cap.read()
 
             self.frame += 1
-            print('video %g/%g (%g/%g) %s: ' % (self.count + 1, self.nf, self.frame, self.nframes, path), end='')
+            logger.debug('video %g/%g (%g/%g) %s: ', self.count + 1, self.nf, self.frame, self.nframes, path)
 
         else:
             # Read image
             self.count += 1
             img0 = cv2.imread(path)  # BGR
             assert img0 is not None, 'Image Not Found ' + path
-            print('image %g/%g %s: ' % (self.count, self.nf, path), end='')
+            logger.debug('image %g/%g %s: ', self.count, self.nf, path)
 
         # Padded resize
         img = letterbox(img0, new_shape=self.img_size)[0]
@@ -234,7 +237,7 @@ def __next__(self):
         # Print
         assert ret_val, 'Camera Error %s' % self.pipe
         img_path = 'webcam.jpg'
-        print('webcam %g: ' % self.count, end='')
+        logger.debug('webcam %g: ', self.count)
 
         # Padded resize
         img = letterbox(img0, new_shape=self.img_size)[0]
@@ -265,7 +268,7 @@ def __init__(self, sources='streams.txt', img_size=640):
         self.sources = sources
         for i, s in enumerate(sources):
             # Start the thread to read frames from the video stream
-            print('%g/%g: %s... ' % (i + 1, n, s), end='')
+            logger.debug('%g/%g: %s... ', i + 1, n, s)
             cap = cv2.VideoCapture(eval(s) if s.isnumeric() else s)
             assert cap.isOpened(), 'Failed to open %s' % s
             w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
@@ -273,15 +276,14 @@ def __init__(self, sources='streams.txt', img_size=640):
             fps = cap.get(cv2.CAP_PROP_FPS) % 100
             _, self.imgs[i] = cap.read()  # guarantee first frame
             thread = Thread(target=self.update, args=([i, cap]), daemon=True)
-            print(' success (%gx%g at %.2f FPS).' % (w, h, fps))
+            logger.debug(' success (%gx%g at %.2f FPS).', w, h, fps)
             thread.start()
-        print('')  # newline
 
         # check for common shapes
         s = np.stack([letterbox(x, new_shape=self.img_size)[0].shape for x in self.imgs], 0)  # inference shapes
         self.rect = np.unique(s, axis=0).shape[0] == 1  # rect inference if all shapes equal
         if not self.rect:
-            print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.')
+            logger.warning('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.')
 
     def update(self, index, cap):
         # Read next stream frame in a daemon thread
@@ -419,7 +421,7 @@ def img2label_paths(img_paths):
                 assert (l >= 0).all(), 'negative labels: %s' % file
                 assert (l[:, 1:] <= 1).all(), 'non-normalized or out of bounds coordinate labels: %s' % file
                 if np.unique(l, axis=0).shape[0] < l.shape[0]:  # duplicate rows
-                    nd += 1  # print('WARNING: duplicate rows in %s' % self.label_files[i])  # duplicate rows
+                    nd += 1  # logger.warning('WARNING: duplicate rows in %s', self.label_files[i])  # duplicate rows
                 if single_cls:
                     l[:, 0] = 0  # force dataset into single-class mode
                 self.labels[i] = l
@@ -456,7 +458,7 @@ def img2label_paths(img_paths):
                         b[[1, 3]] = np.clip(b[[1, 3]], 0, h)
                         assert cv2.imwrite(f, img[b[1]:b[3], b[0]:b[2]]), 'Failure extracting classifier boxes'
             else:
-                ne += 1  # print('empty labels for image %s' % self.img_files[i])  # file empty
+                ne += 1  # logger.info('empty labels for image %s', self.img_files[i])  # file empty
                 # os.system("rm '%s' '%s'" % (self.img_files[i], self.label_files[i]))  # remove
 
             if rank in [-1, 0]:
@@ -464,7 +466,7 @@ def img2label_paths(img_paths):
                     cache_path, nf, nm, ne, nd, n)
         if nf == 0:
             s = 'WARNING: No labels found in %s. See %s' % (os.path.dirname(file) + os.sep, help_url)
-            print(s)
+            logger.info(s)
             assert not augment, '%s. Can not train without labels.' % s
 
         # Cache images into memory for faster training (WARNING: large datasets may exceed system RAM)
@@ -497,7 +499,7 @@ def cache_labels(self, path='labels.cache'):
                     l = np.zeros((0, 5), dtype=np.float32)
                 x[img] = [l, shape]
             except Exception as e:
-                print('WARNING: Ignoring corrupted image and/or label %s: %s' % (img, e))
+                logger.warning('WARNING: Ignoring corrupted image and/or label %s: %s', img, e)
 
         x['hash'] = get_hash(self.label_files + self.img_files)
         torch.save(x, path)  # save for next time
@@ -508,7 +510,7 @@ def __len__(self):
 
     # def __iter__(self):
     #     self.count = -1
-    #     print('ran dataset iter')
+    #     logger.info('ran dataset iter')
     #     #self.shuffled_vector = np.random.permutation(self.nF) if self.augment else np.arange(self.nF)
     #     return self
 

Changed prints to logging in utils/datasets.
I've only changed these ones since that's a part that causes output problems in our tool. I didn't want to change more than that without being able to easily test, sorry.
Usage of info vs debug messages was based purely on common sense

## 🛠️ PR Summary

<sub>Made with ❤️ by [Ultralytics Actions](https://github.com/ultralytics/actions)<sub>

### 🌟 Summary
Refactor print statements to logging in dataset utils.

### 📊 Key Changes
- Replaced `print` statements with `logger` calls to handle messaging in `datasets.py`.
- Imported `logging` to use logging functionality.
- Added a warning log for different stream shapes in video sources.
- Changed info and debug messages to integrate with the logger.
  
### 🎯 Purpose & Impact
- 💡 **Purpose:** The changes introduce a more standard and configurable way to handle output messages within the code, moving from simple print statements to a logging framework.
- 🌍 **Impact to users:**
  - Enhanced ability to control the verbosity of messages by setting different logging levels (e.g., DEBUG, INFO, WARNING).
  - Improved clarity of output messages when running with different execution environments, such as in production or during debugging.
  - Easier integration with monitoring tools and log aggregators that support standard logging protocols.