Update val_batch*.jpg for Chinese fonts
diff --git a/utils/general.py b/utils/general.py
index 86e3b3c1c54..fce5e38c6c9 100755
--- a/utils/general.py
+++ b/utils/general.py
@@ -37,6 +37,7 @@
 ROOT = FILE.parents[1]  # YOLOv5 root directory
 NUM_THREADS = min(8, max(1, os.cpu_count() - 1))  # number of YOLOv5 multiprocessing threads
 VERBOSE = str(os.getenv('YOLOv5_VERBOSE', True)).lower() == 'true'  # global verbose mode
+FONT = 'Arial.ttf'  # https://ultralytics.com/assets/Arial.ttf
 
 torch.set_printoptions(linewidth=320, precision=5, profile='long')
 np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format})  # format short g, %precision=5
@@ -55,6 +56,21 @@ def is_kaggle():
         return False
 
 
+def is_writeable(dir, test=False):
+    # Return True if directory has write permissions, test opening a file with write permissions if test=True
+    if test:  # method 1
+        file = Path(dir) / 'tmp.txt'
+        try:
+            with open(file, 'w'):  # open file with write permissions
+                pass
+            file.unlink()  # remove file
+            return True
+        except OSError:
+            return False
+    else:  # method 2
+        return os.access(dir, os.R_OK)  # possible issues on Windows
+
+
 def set_logging(name=None, verbose=VERBOSE):
     # Sets level and returns logger
     if is_kaggle():
@@ -68,6 +84,22 @@ def set_logging(name=None, verbose=VERBOSE):
 LOGGER = set_logging('yolov5')  # define globally (used in train.py, val.py, detect.py, etc.)
 
 
+def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'):
+    # Return path of user configuration directory. Prefer environment variable if exists. Make dir if required.
+    env = os.getenv(env_var)
+    if env:
+        path = Path(env)  # use environment variable
+    else:
+        cfg = {'Windows': 'AppData/Roaming', 'Linux': '.config', 'Darwin': 'Library/Application Support'}  # 3 OS dirs
+        path = Path.home() / cfg.get(platform.system(), '')  # OS-specific config dir
+        path = (path if is_writeable(path) else Path('/tmp')) / dir  # GCP and AWS lambda fix, only /tmp is writeable
+    path.mkdir(exist_ok=True)  # make if required
+    return path
+
+
+CONFIG_DIR = user_config_dir()  # Ultralytics settings dir
+
+
 class Profile(contextlib.ContextDecorator):
     # Usage: @Profile() decorator or 'with Profile():' context manager
     def __enter__(self):
@@ -152,34 +184,6 @@ def get_latest_run(search_dir='.'):
     return max(last_list, key=os.path.getctime) if last_list else ''
 
 
-def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'):
-    # Return path of user configuration directory. Prefer environment variable if exists. Make dir if required.
-    env = os.getenv(env_var)
-    if env:
-        path = Path(env)  # use environment variable
-    else:
-        cfg = {'Windows': 'AppData/Roaming', 'Linux': '.config', 'Darwin': 'Library/Application Support'}  # 3 OS dirs
-        path = Path.home() / cfg.get(platform.system(), '')  # OS-specific config dir
-        path = (path if is_writeable(path) else Path('/tmp')) / dir  # GCP and AWS lambda fix, only /tmp is writeable
-    path.mkdir(exist_ok=True)  # make if required
-    return path
-
-
-def is_writeable(dir, test=False):
-    # Return True if directory has write permissions, test opening a file with write permissions if test=True
-    if test:  # method 1
-        file = Path(dir) / 'tmp.txt'
-        try:
-            with open(file, 'w'):  # open file with write permissions
-                pass
-            file.unlink()  # remove file
-            return True
-        except OSError:
-            return False
-    else:  # method 2
-        return os.access(dir, os.R_OK)  # possible issues on Windows
-
-
 def is_docker():
     # Is environment a Docker container?
     return Path('/workspace').exists()  # or Path('/.dockerenv').exists()
@@ -207,7 +211,7 @@ def is_ascii(s=''):
 
 def is_chinese(s='人工智能'):
     # Is string composed of any Chinese characters?
-    return re.search('[\u4e00-\u9fff]', s)
+    return True if re.search('[\u4e00-\u9fff]', str(s)) else False
 
 
 def emojis(str=''):
@@ -378,6 +382,15 @@ def check_file(file, suffix=''):
         return files[0]  # return file
 
 
+def check_font(font=FONT):
+    # Download font to CONFIG_DIR if necessary
+    font = Path(font)
+    if not font.exists() and not (CONFIG_DIR / font.name).exists():
+        url = "https://ultralytics.com/assets/" + font.name
+        LOGGER.info(f'Downloading {url} to {CONFIG_DIR / font.name}...')
+        torch.hub.download_url_to_file(url, str(font), progress=False)
+
+
 def check_dataset(data, autodownload=True):
     # Download and/or unzip dataset if not found locally
     # Usage: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128_with_yaml.zip
diff --git a/utils/plots.py b/utils/plots.py
index 74868403edc..be70ac8a030 100644
--- a/utils/plots.py
+++ b/utils/plots.py
@@ -17,12 +17,11 @@
 import torch
 from PIL import Image, ImageDraw, ImageFont
 
-from utils.general import (LOGGER, Timeout, check_requirements, clip_coords, increment_path, is_ascii, is_chinese,
-                           try_except, user_config_dir, xywh2xyxy, xyxy2xywh)
+from utils.general import (CONFIG_DIR, FONT, LOGGER, Timeout, check_font, check_requirements, clip_coords,
+                           increment_path, is_ascii, is_chinese, try_except, xywh2xyxy, xyxy2xywh)
 from utils.metrics import fitness
 
 # Settings
-CONFIG_DIR = user_config_dir()  # Ultralytics settings dir
 RANK = int(os.getenv('RANK', -1))
 matplotlib.rc('font', **{'size': 11})
 matplotlib.use('Agg')  # for writing to files only
@@ -49,16 +48,14 @@ def hex2rgb(h):  # rgb order (PIL)
 colors = Colors()  # create instance for 'from utils.plots import colors'
 
 
-def check_font(font='Arial.ttf', size=10):
+def check_pil_font(font=FONT, size=10):
     # Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary
     font = Path(font)
     font = font if font.exists() else (CONFIG_DIR / font.name)
     try:
         return ImageFont.truetype(str(font) if font.exists() else font.name, size)
     except Exception as e:  # download if missing
-        url = "https://ultralytics.com/assets/" + font.name
-        LOGGER.info(f'Downloading {url} to {font}...')
-        torch.hub.download_url_to_file(url, str(font), progress=False)
+        check_font(font)
         try:
             return ImageFont.truetype(str(font), size)
         except TypeError:
@@ -67,7 +64,7 @@ def check_font(font='Arial.ttf', size=10):
 
 class Annotator:
     if RANK in (-1, 0):
-        check_font()  # download TTF if necessary
+        check_pil_font()  # download TTF if necessary
 
     # YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotations
     def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'):
@@ -76,8 +73,8 @@ def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=Fa
         if self.pil:  # use PIL
             self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
             self.draw = ImageDraw.Draw(self.im)
-            self.font = check_font(font='Arial.Unicode.ttf' if is_chinese(example) else font,
-                                   size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))
+            self.font = check_pil_font(font='Arial.Unicode.ttf' if is_chinese(example) else font,
+                                       size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))
         else:  # use cv2
             self.im = im
         self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2)  # line width
@@ -89,10 +86,10 @@ def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 2
             if label:
                 w, h = self.font.getsize(label)  # text width, height
                 outside = box[1] - h >= 0  # label fits outside box
-                self.draw.rectangle([box[0],
+                self.draw.rectangle((box[0],
                                      box[1] - h if outside else box[1],
                                      box[0] + w + 1,
-                                     box[1] + 1 if outside else box[1] + h + 1], fill=color)
+                                     box[1] + 1 if outside else box[1] + h + 1), fill=color)
                 # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0
                 self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
         else:  # cv2
@@ -210,7 +207,7 @@ def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max
 
     # Annotate
     fs = int((h + w) * ns * 0.01)  # font size
-    annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True)
+    annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names)
     for i in range(i + 1):
         x, y = int(w * (i // ns)), int(h * (i % ns))  # block origin
         annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2)  # borders

Partial fix for #6522

## 🛠️ PR Summary

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

### 🌟 Summary
Improved font handling and directory access in YOLOv5 utilities.

### 📊 Key Changes
- Added a default `FONT` variable specifying the 'Arial.ttf' font.
- Introduced `is_writeable()` function to check directory write permissions.
- Moved `user_config_dir()` function for configuring user-specific settings to an earlier part of the code.
- Created `check_font()` function to ensure the desired font is available, downloading if necessary.
- Modified `check_pil_font()` in `plots.py` to utilize the new `check_font()` from `general.py`.
- Annotator class and `plot_images()` method in `plots.py` were adjusted to use these new font-checking utilities.

### 🎯 Purpose & Impact
- Ensures consistent font usage across different operating environments. 🖋️
- Simplifies checking directory writing permissions, improving file handling. 📁
- Reduces code duplication by consolidating font-related checks. 🛠️
- Enhancements potentially make the software more robust and user-friendly, particularly regarding font dependencies and configuration file management. 👍
- End-users benefit from more reliable output appearance, especially when custom fonts are required. 🌐