danhtran2mind commited on
Commit
694f79d
·
verified ·
1 Parent(s): 26e0a0e

Upload 50 files

Browse files
.gitattributes CHANGED
@@ -37,3 +37,8 @@ assets/lp_video[[:space:]]-[[:space:]]Trim.mp4 filter=lfs diff=lfs merge=lfs -te
37
  assets/lp_video.mp4 filter=lfs diff=lfs merge=lfs -text
38
  apps/assets/examples/license_plate_detector_ocr/2/lp_video_output.mp4 filter=lfs diff=lfs merge=lfs -text
39
  apps/assets/examples/license_plate_detector_ocr/2/lp_video.mp4 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
37
  assets/lp_video.mp4 filter=lfs diff=lfs merge=lfs -text
38
  apps/assets/examples/license_plate_detector_ocr/2/lp_video_output.mp4 filter=lfs diff=lfs merge=lfs -text
39
  apps/assets/examples/license_plate_detector_ocr/2/lp_video.mp4 filter=lfs diff=lfs merge=lfs -text
40
+ apps/gradio_app/assets/examples/license_plate_detector_ocr/2/lp_video_output.mp4 filter=lfs diff=lfs merge=lfs -text
41
+ apps/gradio_app/assets/examples/license_plate_detector_ocr/2/lp_video.mp4 filter=lfs diff=lfs merge=lfs -text
42
+ assets/examples/license_plate_detector_ocr/2/lp_video_output.mp4 filter=lfs diff=lfs merge=lfs -text
43
+ assets/examples/license_plate_detector_ocr/2/lp_video.mp4 filter=lfs diff=lfs merge=lfs -text
44
+ assets/gradio_app_demo.jpg filter=lfs diff=lfs merge=lfs -text
.python-version CHANGED
@@ -1 +1 @@
1
- python v3.11.13
 
1
+ 3.11.13
apps/gradio_app.py CHANGED
@@ -1,138 +1,131 @@
1
- import gradio as gr
2
- import os
3
- import logging
4
- from gradio_app.config import setup_logging, setup_sys_path
5
- from gradio_app.processor import gradio_process, update_preview, update_visibility
6
-
7
- # Initialize logging and sys.path
8
- setup_logging()
9
- setup_sys_path()
10
-
11
- # Configure logging
12
- logger = logging.getLogger(__name__)
13
-
14
- # Load custom CSS
15
- custom_css = """
16
- .custom-table tr:hover { background-color: #f0f0f0; cursor: pointer; }
17
- .custom-table .selected { background-color: #d0e8ff; }
18
- .custom-file-input { margin-bottom: 10px; }
19
- .custom-radio { margin-bottom: 10px; }
20
- .custom-button { margin: 5px; }
21
- .custom-image, .custom-video { width: 100%; }
22
- .custom-textbox { width: 100%; }
23
- """
24
- # Optionally, you can still load from styles.css if preferred
25
- # custom_css = open(os.path.join(os.path.dirname(__file__), "gradio_app", "static", "styles.css"), "r").read()
26
-
27
- # Path to examples directory
28
- examples_dir = os.path.join(os.path.dirname(__file__), "gradio_app", "assets", "examples", "license_plate_detector_ocr")
29
-
30
- # Collect example files
31
- examples = [
32
- {
33
- "Input File": os.path.join(examples_dir, "1", "lp_image.jpg"),
34
- "Output File": os.path.join(examples_dir, "1", "lp_image_output.jpg"),
35
- "Input Type": "Image"
36
- },
37
- {
38
- "Input File": os.path.join(examples_dir, "2", "lp_video.mp4"),
39
- "Output File": os.path.join(examples_dir, "2", "lp_video_output.mp4"),
40
- "Input Type": "Video"
41
- }
42
- ]
43
-
44
- # Validate example file paths
45
- for ex in examples:
46
- if not os.path.exists(ex["Input File"]):
47
- logger.error(f"Input file not found: {ex['Input File']}")
48
- if not os.path.exists(ex["Output File"]):
49
- logger.error(f"Output file not found: {ex['Output File']}")
50
-
51
- # Function to handle example selection
52
- def select_example(selected_row, examples_data):
53
- logger.info(f"Selected row: {selected_row}")
54
- if selected_row is not None:
55
- try:
56
- row_index = int(selected_row[0]) if isinstance(selected_row, list) else int(selected_row)
57
- if 0 <= row_index < len(examples):
58
- selected_example = examples[row_index]
59
- input_file_path = selected_example["Input File"]
60
- logger.info(f"Loading file: {input_file_path}")
61
- return input_file_path, selected_example["Input Type"], None, None, None
62
- except (IndexError, ValueError, TypeError) as e:
63
- logger.error(f"Error selecting example: {e}")
64
- return None, "Image", None, None, None # Default return if selection fails
65
-
66
- # Gradio Interface
67
- with gr.Blocks(css=custom_css) as iface:
68
- gr.Markdown(
69
- """
70
- # License Plate Detection and OCR
71
- Detect license plates from images or videos and read their text using
72
- advanced computer vision and OCR for accurate identification.
73
- """,
74
- elem_classes="markdown-title"
75
- )
76
-
77
- with gr.Row():
78
- with gr.Column(scale=1):
79
- input_file = gr.File(label="Upload Image or Video", file_types=["image", "video"], elem_classes="custom-file-input")
80
- input_type = gr.Radio(choices=["Image", "Video"], label="Input Type", value="Image", elem_classes="custom-radio")
81
- with gr.Blocks():
82
- input_preview_image = gr.Image(label="Input Preview", visible=True, elem_classes="custom-image")
83
- input_preview_video = gr.Video(label="Input Preview", visible=False, elem_classes="custom-video")
84
- with gr.Row():
85
- clear_button = gr.Button("Clear", variant="secondary", elem_classes="custom-button secondary")
86
- submit_button = gr.Button("Submit", variant="primary", elem_classes="custom-button primary")
87
-
88
- # Examples table
89
- gr.Markdown("### Examples")
90
- examples_table = gr.Dataframe(
91
- value=[[os.path.basename(ex["Input File"]), os.path.basename(ex["Output File"]), ex["Input Type"]] for ex in examples],
92
- headers=["Input File", "Output File", "Input Type"],
93
- interactive=True, # Allow row selection
94
- elem_classes="custom-table"
95
- )
96
-
97
- with gr.Column(scale=2):
98
- with gr.Blocks():
99
- output_image = gr.Image(label="Processed Output (Image)", type="numpy", visible=True, elem_classes="custom-image")
100
- output_video = gr.Video(label="Processed Output (Video)", visible=False, elem_classes="custom-video")
101
- output_text = gr.Textbox(label="Detected License Plates", lines=10, elem_classes="custom-textbox")
102
-
103
- # Bind example selection after all components are defined
104
- examples_table.change(
105
- fn=select_example,
106
- inputs=[examples_table, examples_table],
107
- outputs=[input_file, input_type, output_image, output_video, output_text]
108
- )
109
-
110
- # Update preview and output visibility when input type changes
111
- input_type.change(
112
- fn=update_visibility,
113
- inputs=input_type,
114
- outputs=[input_preview_image, input_preview_video, output_image, output_video]
115
- )
116
-
117
- # Update preview when file is uploaded
118
- input_file.change(
119
- fn=update_preview,
120
- inputs=[input_file, input_type],
121
- outputs=[input_preview_image, input_preview_video]
122
- )
123
-
124
- # Bind the processing function
125
- submit_button.click(
126
- fn=gradio_process,
127
- inputs=[input_file, input_type],
128
- outputs=[output_image, output_video, output_text, input_preview_image, input_preview_video]
129
- )
130
-
131
- # Clear button functionality
132
- clear_button.click(
133
- fn=lambda: (None, None, None, "Image", None, None, None, None),
134
- outputs=[input_file, output_image, output_video, input_type, input_preview_image, input_preview_video, output_image, output_video]
135
- )
136
-
137
- if __name__ == "__main__":
138
- iface.launch()
 
1
+ import gradio as gr
2
+ import os
3
+ from gradio_app.config import setup_logging, setup_sys_path
4
+ from gradio_app.processor import gradio_process, update_preview, update_visibility, clear_preview_data
5
+
6
+ # Initialize logging and sys.path
7
+ setup_logging()
8
+ setup_sys_path()
9
+
10
+ # Load custom CSS
11
+ custom_css = open(os.path.join(os.path.dirname(__file__), "gradio_app", "static", "styles.css"), "r").read()
12
+
13
+ # Define example files
14
+ examples = [
15
+ {
16
+ "input_file": os.path.join(os.path.dirname(__file__), "gradio_app", "assets", "examples", "license_plate_detector_ocr", "1", "lp_image.jpg"),
17
+ "output_file": os.path.join(os.path.dirname(__file__), "gradio_app", "assets", "examples", "license_plate_detector_ocr", "1", "lp_image_output.jpg"),
18
+ "input_type": "Image"
19
+ },
20
+ {
21
+ "input_file": os.path.join(os.path.dirname(__file__), "gradio_app", "assets", "examples", "license_plate_detector_ocr", "2", "lp_video.mp4"),
22
+ "output_file": os.path.join(os.path.dirname(__file__), "gradio_app", "assets", "examples", "license_plate_detector_ocr", "2", "lp_video_output.mp4"),
23
+ "input_type": "Video"
24
+ }
25
+ ]
26
+
27
+ # Function to handle example selection
28
+ def load_example(evt: gr.SelectData):
29
+ index = evt.index[0] if evt.index else 0
30
+ example = examples[index]
31
+ input_file = example["input_file"]
32
+ output_file = example["output_file"]
33
+ input_type = example["input_type"]
34
+
35
+ # Update visibility based on input type
36
+ input_preview_image, input_preview_video, output_image, output_video = update_visibility(input_type)
37
+
38
+ # Update preview based on input file and type
39
+ input_preview_image, input_preview_video = update_preview(input_file, input_type)
40
+
41
+ return (
42
+ input_file,
43
+ input_type,
44
+ input_preview_image,
45
+ input_preview_video,
46
+ output_file if input_type == "Image" else None,
47
+ output_file if input_type == "Video" else None,
48
+ "Example loaded - click Submit to process"
49
+ )
50
+
51
+ # Gradio Interface
52
+ with gr.Blocks(css=custom_css) as iface:
53
+ gr.Markdown(
54
+ """
55
+ # License Plate Detection and OCR
56
+ Detect license plates from images or videos and read their text using
57
+ advanced computer vision and OCR for accurate identification.
58
+ """,
59
+ elem_classes="markdown-title"
60
+ )
61
+
62
+
63
+ with gr.Row():
64
+ with gr.Column(scale=1):
65
+ input_file = gr.File(label="Upload Image or Video", elem_classes="custom-file-input")
66
+ input_type = gr.Radio(choices=["Image", "Video"], label="Input Type", value="Image", elem_classes="custom-radio")
67
+ with gr.Blocks():
68
+ input_preview_image = gr.Image(label="Input Preview", visible=True, elem_classes="custom-image")
69
+ input_preview_video = gr.Video(label="Input Preview", visible=False, elem_classes="custom-video")
70
+ with gr.Row():
71
+ clear_button = gr.Button("Clear", variant="secondary", elem_classes="custom-button secondary")
72
+ submit_button = gr.Button("Submit", variant="primary", elem_classes="custom-button primary")
73
+ with gr.Column(scale=1):
74
+ with gr.Blocks():
75
+ output_image = gr.Image(label="Processed Output (Image)", type="numpy", visible=True, elem_classes="custom-image")
76
+ output_video = gr.Video(label="Processed Output (Video)", visible=False, elem_classes="custom-video")
77
+ output_text = gr.Textbox(label="Detected License Plates", lines=10, elem_classes="custom-textbox")
78
+
79
+ # Update preview and output visibility when input type changes
80
+ input_type.change(
81
+ fn=update_visibility,
82
+ inputs=input_type,
83
+ outputs=[input_preview_image, input_preview_video, output_image, output_video]
84
+ )
85
+
86
+ # Update preview when file is uploaded
87
+ input_file.change(
88
+ fn=update_preview,
89
+ inputs=[input_file, input_type],
90
+ outputs=[input_preview_image, input_preview_video]
91
+ )
92
+
93
+ # Bind the processing function
94
+ submit_button.click(
95
+ fn=gradio_process,
96
+ inputs=[input_file, input_type],
97
+ outputs=[output_image, output_video, output_text, input_preview_image, input_preview_video]
98
+ )
99
+
100
+ # Clear button functionality
101
+ clear_button.click(
102
+ fn=lambda: (None, None, None, "Image", None, None, None, None),
103
+ outputs=[input_file, output_image, output_video, input_type, input_preview_image, input_preview_video, output_image, output_video]
104
+ ).then(
105
+ fn=clear_preview_data,
106
+ inputs=None,
107
+ outputs=None
108
+ )
109
+
110
+ # Examples table
111
+ with gr.Row():
112
+ gr.Markdown("### Examples")
113
+
114
+ with gr.Row():
115
+ example_table = gr.Dataframe(
116
+ value=[[i, ex["input_type"], os.path.basename(ex["input_file"])] for i, ex in enumerate(examples)],
117
+ headers=["Index", "Type", "File"],
118
+ datatype=["number", "str", "str"],
119
+ interactive=True,
120
+ elem_classes="custom-table"
121
+ )
122
+
123
+ # Example table click handler
124
+ example_table.select(
125
+ fn=load_example,
126
+ inputs=None,
127
+ outputs=[input_file, input_type, input_preview_image, input_preview_video, output_image, output_video, output_text]
128
+ )
129
+
130
+ if __name__ == "__main__":
131
+ iface.launch(share=True)
 
 
 
 
 
 
 
apps/gradio_app/assets/examples/license_plate_detector_ocr/1/lp_image.jpg ADDED
apps/gradio_app/assets/examples/license_plate_detector_ocr/1/lp_image_output.jpg ADDED
apps/gradio_app/assets/examples/license_plate_detector_ocr/2/lp_video.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:72dececeb4cc1ce1da5264211578c9331a3fb31d36bf21ac2f40471d70e2121d
3
+ size 4984385
apps/gradio_app/assets/examples/license_plate_detector_ocr/2/lp_video_output.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:72dececeb4cc1ce1da5264211578c9331a3fb31d36bf21ac2f40471d70e2121d
3
+ size 4984385
apps/gradio_app/processor.py CHANGED
@@ -4,7 +4,9 @@ import shutil
4
  import traceback
5
  import logging
6
  import gradio as gr
7
- import uuid # Import uuid module
 
 
8
  from gradio_app.utils import convert_to_supported_format
9
 
10
  # Adjust sys.path to include the src directory
@@ -13,60 +15,125 @@ from infer import infer, is_image_file
13
 
14
  def gradio_process(input_file, input_type):
15
  """Process the input file (image or video) for license plate detection and OCR."""
 
 
 
16
  try:
17
- logging.debug(f"Input file path: {input_file.name}")
18
- print(f"Input file path: {input_file.name}")
 
19
 
20
- # Copy input file to temp_data directory to ensure stability
21
- temp_input_dir = "apps/gradio_app/temp_data"
22
- os.makedirs(temp_input_dir, exist_ok=True)
23
- temp_input_path = os.path.join(temp_input_dir, os.path.basename(input_file.name))
24
- shutil.copy(input_file.name, temp_input_path)
25
- logging.debug(f"Copied input file to: {temp_input_path}")
26
-
27
- # Verify input file exists
28
- if not os.path.exists(temp_input_path):
29
- error_msg = f"Error: Input file {temp_input_path} does not exist."
30
  logging.error(error_msg)
31
  return None, None, error_msg, None, None
32
 
33
- # Set output path with UUID
34
- output_dir = "apps/gradio_app/temp_data"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  os.makedirs(output_dir, exist_ok=True)
36
- # Modified line with UUID
37
- unique_id = str(uuid.uuid4())[:8] # Use first 8 characters of UUID for brevity
38
  output_filename = f"{os.path.splitext(os.path.basename(temp_input_path))[0]}_{unique_id}_output{'_output.jpg' if is_image_file(temp_input_path) else '_output.mp4'}"
39
  output_path = os.path.join(output_dir, output_filename)
40
  logging.debug(f"Output path: {output_path}")
41
 
42
  # Call the infer function
 
43
  result_array, plate_texts = infer(temp_input_path, output_path)
44
 
45
  if result_array is None and is_image_file(temp_input_path):
46
- error_msg = f"Error: Processing failed for {temp_input_path}. 'infer' returned None."
47
  logging.error(error_msg)
48
- return None, None, error_msg, None, None
49
 
50
  # Validate output file for videos
51
  if not is_image_file(temp_input_path):
52
  if not os.path.exists(output_path):
53
  error_msg = f"Error: Output video file {output_path} was not created."
54
  logging.error(error_msg)
55
- return None, None, error_msg, None, None
56
  # Convert output video to supported format
57
  converted_output_path = os.path.join(output_dir, f"converted_{os.path.basename(output_path)}")
58
  converted_path = convert_to_supported_format(output_path, converted_output_path)
59
  if converted_path is None:
60
  error_msg = f"Error: Failed to convert output video {output_path} to supported format."
61
  logging.error(error_msg)
62
- return None, None, error_msg, None, None
63
  output_path = converted_path
64
 
65
  # Format plate texts
66
  if is_image_file(temp_input_path):
67
  formatted_texts = "\n".join(plate_texts) if plate_texts else "No plates detected"
68
  logging.debug(f"Image processed successfully. Plate texts: {formatted_texts}")
69
- return result_array, None, formatted_texts, temp_input_path, None
70
  else:
71
  formatted_texts = []
72
  for i, texts in enumerate(plate_texts):
@@ -74,28 +141,52 @@ def gradio_process(input_file, input_type):
74
  formatted_texts.append(f"Frame {i+1}: {', '.join(texts)}")
75
  formatted_texts = "\n".join(formatted_texts) if formatted_texts else "No plates detected"
76
  logging.debug(f"Video processed successfully. Plate texts: {formatted_texts}")
77
- return None, output_path, formatted_texts, None, temp_input_path
78
  except Exception as e:
79
- error_message = f"Error processing {input_file.name}: {str(e)}\n{traceback.format_exc()}"
80
  logging.error(error_message)
81
  print(error_message)
82
- return None, None, error_message, None, None
 
 
 
 
 
83
 
84
  def update_preview(file, input_type):
85
  """Return file path for the appropriate preview component based on input type."""
86
  if not file:
87
  logging.debug("No file provided for preview.")
88
  return None, None
89
- logging.debug(f"Updating preview for {input_type}: {file.name}")
 
 
 
 
90
  # Verify file exists
91
- if not os.path.exists(file.name):
92
- logging.error(f"Input file {file.name} does not exist.")
93
  return None, None
 
94
  # Check if video format is supported
95
- if input_type == "Video" and not file.name.lower().endswith(('.mp4', '.webm')):
96
- logging.error(f"Unsupported video format for {file.name}. Use MP4 or WebM.")
97
  return None, None
98
- return file.name if input_type == "Image" else None, file.name if input_type == "Video" else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  def update_visibility(input_type):
101
  """Update visibility of input/output components based on input type."""
@@ -107,4 +198,12 @@ def update_visibility(input_type):
107
  gr.update(visible=is_video),
108
  gr.update(visible=is_image),
109
  gr.update(visible=is_video)
110
- )
 
 
 
 
 
 
 
 
 
4
  import traceback
5
  import logging
6
  import gradio as gr
7
+ import uuid
8
+ import cv2
9
+ import time
10
  from gradio_app.utils import convert_to_supported_format
11
 
12
  # Adjust sys.path to include the src directory
 
15
 
16
  def gradio_process(input_file, input_type):
17
  """Process the input file (image or video) for license plate detection and OCR."""
18
+ unique_id = str(uuid.uuid4())[:8]
19
+ temp_input_dir = os.path.abspath(os.path.join("apps/gradio_app/temp_data", unique_id))
20
+ preview_dir = os.path.abspath(os.path.join("apps/gradio_app/preview_data", unique_id))
21
  try:
22
+ file_path = input_file.name if hasattr(input_file, 'name') else input_file
23
+ logging.debug(f"Input file path: {file_path}")
24
+ print(f"Input file path: {file_path}")
25
 
26
+ # Verify source file exists and is readable
27
+ if not os.path.exists(file_path):
28
+ error_msg = f"Error: Source file {file_path} does not exist."
29
+ logging.error(error_msg)
30
+ return None, None, error_msg, None, None
31
+ if not os.access(file_path, os.R_OK):
32
+ error_msg = f"Error: Source file {file_path} is not readable."
 
 
 
33
  logging.error(error_msg)
34
  return None, None, error_msg, None, None
35
 
36
+ # Create unique temp and preview directories
37
+ os.makedirs(temp_input_dir, exist_ok=True)
38
+ os.makedirs(preview_dir, exist_ok=True)
39
+ temp_input_path = os.path.join(temp_input_dir, os.path.basename(file_path))
40
+ preview_input_path = os.path.join(preview_dir, os.path.basename(file_path))
41
+
42
+ # Copy input file to temp and preview directories with retry
43
+ max_retries = 3
44
+ for attempt in range(max_retries):
45
+ try:
46
+ shutil.copy2(file_path, temp_input_path) # Copy to temp for processing
47
+ shutil.copy2(file_path, preview_input_path) # Copy to preview for display
48
+ os.chmod(temp_input_path, 0o644)
49
+ os.chmod(preview_input_path, 0o644)
50
+ logging.debug(f"Copied input file to: {temp_input_path} and {preview_input_path}")
51
+ break
52
+ except Exception as e:
53
+ if attempt == max_retries - 1:
54
+ error_msg = f"Error copying file {file_path} to {temp_input_path} or {preview_input_path} after {max_retries} attempts: {str(e)}"
55
+ logging.error(error_msg)
56
+ return None, None, error_msg, None, None
57
+ time.sleep(0.5) # Brief delay before retry
58
+
59
+ # Verify copied files
60
+ for path in [temp_input_path, preview_input_path]:
61
+ if not os.path.exists(path):
62
+ error_msg = f"Error: Copied file {path} does not exist."
63
+ logging.error(error_msg)
64
+ return None, None, error_msg, None, None
65
+ if not os.access(path, os.R_OK):
66
+ error_msg = f"Error: Copied file {path} is not readable."
67
+ logging.error(error_msg)
68
+ return None, None, error_msg, None, None
69
+ if os.path.getsize(path) == 0:
70
+ error_msg = f"Error: Copied file {path} is empty."
71
+ logging.error(error_msg)
72
+ return None, None, error_msg, None, None
73
+
74
+ # Validate image or video
75
+ if is_image_file(temp_input_path):
76
+ img = cv2.imread(temp_input_path)
77
+ if img is None:
78
+ error_msg = f"Error: Could not load image from {temp_input_path}."
79
+ logging.error(error_msg)
80
+ return None, None, error_msg, None, None
81
+ # Check image properties
82
+ height, width, channels = img.shape
83
+ logging.debug(f"Image properties: {width}x{height}, {channels} channels")
84
+ if channels not in (1, 3, 4):
85
+ error_msg = f"Error: Unsupported number of channels ({channels}) in {temp_input_path}. Expected 1, 3, or 4."
86
+ logging.error(error_msg)
87
+ return None, None, error_msg, None, None
88
+ if width == 0 or height == 0:
89
+ error_msg = f"Error: Invalid image dimensions ({width}x{height}) in {temp_input_path}."
90
+ logging.error(error_msg)
91
+ return None, None, error_msg, None, None
92
+ else:
93
+ cap = cv2.VideoCapture(temp_input_path)
94
+ if not cap.isOpened():
95
+ error_msg = f"Error: Could not open video at {temp_input_path}."
96
+ logging.error(error_msg)
97
+ cap.release()
98
+ return None, None, error_msg, None, None
99
+ cap.release()
100
+
101
+ # Set output path
102
+ output_dir = os.path.abspath(os.path.join("apps/gradio_app/temp_data", str(uuid.uuid4())[:8]))
103
  os.makedirs(output_dir, exist_ok=True)
 
 
104
  output_filename = f"{os.path.splitext(os.path.basename(temp_input_path))[0]}_{unique_id}_output{'_output.jpg' if is_image_file(temp_input_path) else '_output.mp4'}"
105
  output_path = os.path.join(output_dir, output_filename)
106
  logging.debug(f"Output path: {output_path}")
107
 
108
  # Call the infer function
109
+ logging.debug(f"Calling infer with input: {temp_input_path}, output: {output_path}")
110
  result_array, plate_texts = infer(temp_input_path, output_path)
111
 
112
  if result_array is None and is_image_file(temp_input_path):
113
+ error_msg = f"Error: Processing failed for {temp_input_path}. 'infer' returned None. Check infer.py logs for details."
114
  logging.error(error_msg)
115
+ return None, None, error_msg, preview_input_path if is_image_file(temp_input_path) else None, preview_input_path if not is_image_file(temp_input_path) else None
116
 
117
  # Validate output file for videos
118
  if not is_image_file(temp_input_path):
119
  if not os.path.exists(output_path):
120
  error_msg = f"Error: Output video file {output_path} was not created."
121
  logging.error(error_msg)
122
+ return None, None, error_msg, None, preview_input_path
123
  # Convert output video to supported format
124
  converted_output_path = os.path.join(output_dir, f"converted_{os.path.basename(output_path)}")
125
  converted_path = convert_to_supported_format(output_path, converted_output_path)
126
  if converted_path is None:
127
  error_msg = f"Error: Failed to convert output video {output_path} to supported format."
128
  logging.error(error_msg)
129
+ return None, None, error_msg, None, preview_input_path
130
  output_path = converted_path
131
 
132
  # Format plate texts
133
  if is_image_file(temp_input_path):
134
  formatted_texts = "\n".join(plate_texts) if plate_texts else "No plates detected"
135
  logging.debug(f"Image processed successfully. Plate texts: {formatted_texts}")
136
+ return result_array, None, formatted_texts, preview_input_path, None
137
  else:
138
  formatted_texts = []
139
  for i, texts in enumerate(plate_texts):
 
141
  formatted_texts.append(f"Frame {i+1}: {', '.join(texts)}")
142
  formatted_texts = "\n".join(formatted_texts) if formatted_texts else "No plates detected"
143
  logging.debug(f"Video processed successfully. Plate texts: {formatted_texts}")
144
+ return None, output_path, formatted_texts, None, preview_input_path
145
  except Exception as e:
146
+ error_message = f"Error processing {file_path}: {str(e)}\n{traceback.format_exc()}"
147
  logging.error(error_message)
148
  print(error_message)
149
+ return None, None, error_message, preview_input_path if is_image_file(file_path) else None, preview_input_path if not is_image_file(file_path) else None
150
+ finally:
151
+ # Clean up temp directory after processing, but keep preview directory
152
+ if os.path.exists(temp_input_dir):
153
+ shutil.rmtree(temp_input_dir, ignore_errors=True)
154
+ logging.debug(f"Cleaned up temporary directory: {temp_input_dir}")
155
 
156
  def update_preview(file, input_type):
157
  """Return file path for the appropriate preview component based on input type."""
158
  if not file:
159
  logging.debug("No file provided for preview.")
160
  return None, None
161
+
162
+ # Handle both file objects and string paths
163
+ file_path = file.name if hasattr(file, 'name') else file
164
+ logging.debug(f"Updating preview for {input_type}: {file_path}")
165
+
166
  # Verify file exists
167
+ if not os.path.exists(file_path):
168
+ logging.error(f"Input file {file_path} does not exist.")
169
  return None, None
170
+
171
  # Check if video format is supported
172
+ if input_type == "Video" and not file_path.lower().endswith(('.mp4', '.webm')):
173
+ logging.error(f"Unsupported video format for {file_path}. Use MP4 or WebM.")
174
  return None, None
175
+
176
+ # Copy to preview directory for persistent display
177
+ unique_id = str(uuid.uuid4())[:8]
178
+ preview_dir = os.path.abspath(os.path.join("apps/gradio_app/preview_data", unique_id))
179
+ os.makedirs(preview_dir, exist_ok=True)
180
+ preview_input_path = os.path.join(preview_dir, os.path.basename(file_path))
181
+ try:
182
+ shutil.copy2(file_path, preview_input_path)
183
+ os.chmod(preview_input_path, 0o644)
184
+ logging.debug(f"Copied preview file to: {preview_input_path}")
185
+ except Exception as e:
186
+ logging.error(f"Error copying preview file to {preview_input_path}: {str(e)}")
187
+ return None, None
188
+
189
+ return preview_input_path if input_type == "Image" else None, preview_input_path if input_type == "Video" else None
190
 
191
  def update_visibility(input_type):
192
  """Update visibility of input/output components based on input type."""
 
198
  gr.update(visible=is_video),
199
  gr.update(visible=is_image),
200
  gr.update(visible=is_video)
201
+ )
202
+
203
+ def clear_preview_data():
204
+ """Clear all files in the preview_data directory."""
205
+ preview_data_dir = os.path.abspath("apps/gradio_app/preview_data")
206
+ if os.path.exists(preview_data_dir):
207
+ shutil.rmtree(preview_data_dir, ignore_errors=True)
208
+ logging.debug(f"Cleared preview_data directory: {preview_data_dir}")
209
+ os.makedirs(preview_data_dir, exist_ok=True)
apps/old3-gradio_app.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from gradio_app.config import setup_logging, setup_sys_path
4
+ from gradio_app.processor import gradio_process, update_preview, update_visibility
5
+
6
+ # Initialize logging and sys.path
7
+ setup_logging()
8
+ setup_sys_path()
9
+
10
+ # Load custom CSS
11
+ custom_css = open(os.path.join(os.path.dirname(__file__), "gradio_app", "static", "styles.css"), "r").read()
12
+
13
+ # Gradio Interface
14
+ with gr.Blocks(css=custom_css) as iface:
15
+ gr.Markdown(
16
+ """
17
+ # License Plate Detection and OCR
18
+ Detect license plates from images or videos and read their text using
19
+ advanced computer vision and OCR for accurate identification.
20
+ """,
21
+ elem_classes="markdown-title"
22
+ )
23
+
24
+ with gr.Row():
25
+ with gr.Column(scale=1):
26
+ input_file = gr.File(label="Upload Image or Video", elem_classes="custom-file-input")
27
+ input_type = gr.Radio(choices=["Image", "Video"], label="Input Type", value="Image", elem_classes="custom-radio")
28
+ with gr.Blocks():
29
+ input_preview_image = gr.Image(label="Input Preview", visible=True, elem_classes="custom-image")
30
+ input_preview_video = gr.Video(label="Input Preview", visible=False, elem_classes="custom-video")
31
+ with gr.Row():
32
+ clear_button = gr.Button("Clear", variant="secondary", elem_classes="custom-button secondary")
33
+ submit_button = gr.Button("Submit", variant="primary", elem_classes="custom-button primary")
34
+ with gr.Column(scale=2):
35
+ with gr.Blocks():
36
+ output_image = gr.Image(label="Processed Output (Image)", type="numpy", visible=True, elem_classes="custom-image")
37
+ output_video = gr.Video(label="Processed Output (Video)", visible=False, elem_classes="custom-video")
38
+ output_text = gr.Textbox(label="Detected License Plates", lines=10, elem_classes="custom-textbox")
39
+
40
+ # Update preview and output visibility when input type changes
41
+ input_type.change(
42
+ fn=update_visibility,
43
+ inputs=input_type,
44
+ outputs=[input_preview_image, input_preview_video, output_image, output_video]
45
+ )
46
+
47
+ # Update preview when file is uploaded
48
+ input_file.change(
49
+ fn=update_preview,
50
+ inputs=[input_file, input_type],
51
+ outputs=[input_preview_image, input_preview_video]
52
+ )
53
+
54
+ # Bind the processing function
55
+ submit_button.click(
56
+ fn=gradio_process,
57
+ inputs=[input_file, input_type],
58
+ outputs=[output_image, output_video, output_text, input_preview_image, input_preview_video]
59
+ )
60
+
61
+ # Clear button functionality
62
+ clear_button.click(
63
+ fn=lambda: (None, None, None, "Image", None, None, None, None),
64
+ outputs=[input_file, output_image, output_video, input_type, input_preview_image, input_preview_video, output_image, output_video]
65
+ )
66
+
67
+ if __name__ == "__main__":
68
+ iface.launch(share=True)
assets/examples/license_plate_detector_ocr/1/lp_image.jpg ADDED
assets/examples/license_plate_detector_ocr/1/lp_image_output.jpg ADDED
assets/examples/license_plate_detector_ocr/2/lp_video.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:72dececeb4cc1ce1da5264211578c9331a3fb31d36bf21ac2f40471d70e2121d
3
+ size 4984385
assets/examples/license_plate_detector_ocr/2/lp_video_output.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:72dececeb4cc1ce1da5264211578c9331a3fb31d36bf21ac2f40471d70e2121d
3
+ size 4984385
assets/gradio_app_demo.jpg ADDED

Git LFS Details

  • SHA256: b7c1965d3d506656b43fc396a8f2c5b120d89826b49be15b3cde24ae4560f66e
  • Pointer size: 131 Bytes
  • Size of remote file: 336 kB
requirements/requirements.txt CHANGED
@@ -1,8 +1,9 @@
1
- opencv-python
2
- ultralytics
3
- roboflow
4
  wget
 
5
  ffmpeg-python
6
- paddleocr
7
- paddlepaddle-gpu
8
- paddlepaddle
 
1
+ opencv-python>=4.11.0.86
2
+ ultralytics>=8.3.162
3
+ roboflow>=1.1.66
4
  wget
5
+ albumentations==2.0.8
6
  ffmpeg-python
7
+ paddleocr==2.9.0
8
+ paddlepaddle-gpu==2.6.2
9
+ paddlepaddle==2.6.2
requirements/requirements_compatible.txt CHANGED
@@ -2,6 +2,7 @@ opencv-python==4.11.0.86
2
  ultralytics==8.3.162
3
  roboflow==1.1.66
4
  wget==3.2
 
5
  ffmpeg-python==0.2.0
6
  paddleocr==2.9.0
7
  paddlepaddle-gpu==2.6.2
 
2
  ultralytics==8.3.162
3
  roboflow==1.1.66
4
  wget==3.2
5
+ albumentations==2.0.8
6
  ffmpeg-python==0.2.0
7
  paddleocr==2.9.0
8
  paddlepaddle-gpu==2.6.2
src/license_plate_detector_ocr/infer.py CHANGED
@@ -1,168 +1,54 @@
1
  import os
2
  import sys
3
- import cv2
4
- import numpy as np
5
- from ultralytics import YOLO
6
- from inference.paddleocr_infer import process_ocr
7
 
8
  # Append the current directory to sys.path
9
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__))))
10
 
11
  def is_image_file(file_path):
12
  """Check if the file is an image based on its extension."""
13
  image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff'}
14
  return os.path.splitext(file_path)[1].lower() in image_extensions
15
 
16
- def process_image(model, image_path, output_path=None):
17
- """Process a single image for license plate detection and OCR."""
18
- image = cv2.imread(image_path)
19
- if image is None:
20
- print(f"Error: Could not load image from {image_path}")
21
- return None, None
22
-
23
- try:
24
- results = model(image_path)
25
- except Exception as e:
26
- print(f"Error during image inference: {e}")
27
- return None, None
28
-
29
- plate_texts = []
30
- for result in results:
31
- for box in result.boxes:
32
- x1, y1, x2, y2 = map(int, box.xyxy[0])
33
- confidence = box.conf[0]
34
-
35
- # Crop the license plate region
36
- plate_region = image[y1:y2, x1:x2]
37
- # Run OCR on the cropped region
38
- ocr_results = process_ocr(plate_region)
39
- plate_text = ocr_results[0] if ocr_results else "No text detected"
40
- plate_texts.append(plate_text)
41
-
42
- # Draw bounding box and OCR text on the image
43
- cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
44
- label = f"{plate_text} ({confidence:.2f})"
45
- cv2.putText(image, label, (x1, y1 - 10),
46
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
47
-
48
- # Set default output path if not provided
49
- if output_path is None:
50
- output_path = os.path.splitext(image_path)[0] + '_output.jpg'
51
-
52
- # Ensure output directory exists
53
- os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
54
- cv2.imwrite(output_path, image)
55
- print(f"Saved processed image to {output_path}")
56
-
57
- return image, plate_texts
58
-
59
- def process_video(model, video_path, output_path=None):
60
- """Process a video for license plate detection and OCR, writing text on detected boxes."""
61
- cap = cv2.VideoCapture(video_path)
62
- if not cap.isOpened():
63
- print(f"Error: Could not open video at {video_path}")
64
- return None, None
65
-
66
- # Get video properties
67
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
68
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
69
- fps = int(cap.get(cv2.CAP_PROP_FPS))
70
-
71
- # Set default output path if not provided
72
- if output_path is None:
73
- output_path = os.path.splitext(video_path)[0] + '_output.mp4'
74
-
75
- # Ensure output directory exists
76
- os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
77
-
78
- # Prepare output video
79
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
80
- out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
81
-
82
- frames = []
83
- all_plate_texts = []
84
-
85
- while cap.isOpened():
86
- ret, frame = cap.read()
87
- if not ret:
88
- print("End of video or error reading frame.")
89
- break
90
-
91
- try:
92
- results = model(frame)
93
- except Exception as e:
94
- print(f"Error during video inference: {e}")
95
- break
96
-
97
- frame_plate_texts = []
98
- boxes_detected = False
99
-
100
- for result in results:
101
- for box in result.boxes:
102
- boxes_detected = True
103
- x1, y1, x2, y2 = map(int, box.xyxy[0])
104
- confidence = box.conf[0]
105
-
106
- # Crop the license plate region
107
- plate_region = frame[y1:y2, x1:x2]
108
-
109
- # Run OCR on the cropped region
110
- ocr_results = process_ocr(plate_region)
111
- plate_text = ocr_results[0] if ocr_results else "No text detected"
112
- frame_plate_texts.append(plate_text)
113
-
114
- # Draw bounding box and OCR text on the frame
115
- cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
116
- label = f"{plate_text} ({confidence:.2f})"
117
- cv2.putText(frame, label, (x1, y1 - 10),
118
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
119
-
120
- if boxes_detected:
121
- frames.append(frame)
122
- all_plate_texts.append(frame_plate_texts)
123
- else:
124
- # Append frame even if no boxes detected to maintain video continuity
125
- frames.append(frame)
126
- all_plate_texts.append([])
127
-
128
- out.write(frame)
129
-
130
- cap.release()
131
- out.release()
132
- print(f"Saved processed video to {output_path}")
133
-
134
- if not frames:
135
- print("No frames processed.")
136
- return None, None
137
-
138
- # Convert list of frames to 4D NumPy array
139
- video_array = np.stack(frames, axis=0)
140
- return video_array, all_plate_texts
141
-
142
  def infer(input_path, output_path=None):
143
- """Main function to process either an image or video for license plate detection and OCR."""
144
  model_path = "ckpts/yolo/finetune/runs/license_plate_detector/weights/best.pt"
145
 
 
 
146
  if not os.path.exists(model_path):
147
- print(f"Error: Model file not found at {model_path}")
 
 
148
  return None, None
149
 
150
  if not os.path.exists(input_path):
151
- print(f"Error: Input file not found at {input_path}")
 
 
152
  return None, None
153
 
154
  try:
155
- model = YOLO(model_path)
 
 
 
 
 
 
 
 
 
 
 
 
156
  except Exception as e:
157
- print(f"Error loading model: {e}")
 
 
158
  return None, None
159
-
160
- if is_image_file(input_path):
161
- result_array, plate_texts = process_image(model, input_path, output_path)
162
- else:
163
- result_array, plate_texts = process_video(model, video_path=input_path, output_path=output_path)
164
-
165
- return result_array, plate_texts
166
 
167
  if __name__ == "__main__":
168
  import argparse
 
1
  import os
2
  import sys
3
+ import logging
4
+ import traceback
5
+ from inference.image_video_processor import process_image, process_video
 
6
 
7
  # Append the current directory to sys.path
8
+ sys.path.append(os.path.abspath(os.path.dirname(__file__)))
9
 
10
  def is_image_file(file_path):
11
  """Check if the file is an image based on its extension."""
12
  image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff'}
13
  return os.path.splitext(file_path)[1].lower() in image_extensions
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  def infer(input_path, output_path=None):
16
+ """Process an image or video for license plate detection and OCR."""
17
  model_path = "ckpts/yolo/finetune/runs/license_plate_detector/weights/best.pt"
18
 
19
+ logging.debug(f"Starting inference for {input_path} with output {output_path}")
20
+
21
  if not os.path.exists(model_path):
22
+ error_msg = f"Error: Model file not found at {model_path}"
23
+ logging.error(error_msg)
24
+ print(error_msg)
25
  return None, None
26
 
27
  if not os.path.exists(input_path):
28
+ error_msg = f"Error: Input file not found at {input_path}"
29
+ logging.error(error_msg)
30
+ print(error_msg)
31
  return None, None
32
 
33
  try:
34
+ if is_image_file(input_path):
35
+ result_array, plate_texts = process_image(model_path, input_path, output_path)
36
+ else:
37
+ result_array, plate_texts = process_video(model_path, input_path, output_path)
38
+
39
+ if result_array is None:
40
+ error_msg = f"Error: Processing failed in {'process_image' if is_image_file(input_path) else 'process_video'} for {input_path}"
41
+ logging.error(error_msg)
42
+ print(error_msg)
43
+ return None, None
44
+
45
+ logging.debug(f"Inference successful: {len(plate_texts)} plates detected")
46
+ return result_array, plate_texts
47
  except Exception as e:
48
+ error_msg = f"Error during inference for {input_path}: {str(e)}\n{traceback.format_exc()}"
49
+ logging.error(error_msg)
50
+ print(error_msg)
51
  return None, None
 
 
 
 
 
 
 
52
 
53
  if __name__ == "__main__":
54
  import argparse
src/license_plate_detector_ocr/inference/image_video_processor.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ from uuid import uuid4
5
+ from inference.yolo_infer import yolo_infer
6
+ from inference.paddleocr_infer import process_ocr
7
+
8
+ def process_image(model_path, image_path, output_path=None):
9
+ """Process a single image for license plate detection and OCR."""
10
+ image = cv2.imread(image_path)
11
+ if image is None:
12
+ print(f"Error: Could not load image from {image_path}")
13
+ return None, None
14
+
15
+ try:
16
+ results = yolo_infer(model_path, image_path)
17
+ except Exception as e:
18
+ print(f"Error during image inference: {e}")
19
+ return None, None
20
+
21
+ plate_texts = []
22
+ for result in results:
23
+ for box in result.boxes:
24
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
25
+ confidence = box.conf[0]
26
+
27
+ # Crop the license plate region
28
+ plate_region = image[y1:y2, x1:x2]
29
+ # Run OCR on the cropped region
30
+ ocr_results = process_ocr(plate_region)
31
+ plate_text = ocr_results[0] if ocr_results else "No text detected"
32
+ plate_texts.append(plate_text)
33
+
34
+ # Draw bounding box and OCR text on the image
35
+ cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
36
+ label = f"{plate_text} ({confidence:.2f})"
37
+ cv2.putText(image, label, (x1, y1 - 10),
38
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
39
+
40
+ # Set default output path with UUID if not provided
41
+ if output_path is None:
42
+ output_dir = "apps/gradio_app/temp_data"
43
+ output_path = os.path.join(output_dir, f"output_{uuid4()}.jpg")
44
+
45
+ # Ensure output directory exists
46
+ os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
47
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
48
+ cv2.imwrite(output_path, image)
49
+ print(f"Saved processed image to {output_path}")
50
+
51
+ return image, plate_texts
52
+
53
+ def process_video(model_path, video_path, output_path=None):
54
+ """Process a video for license plate detection and OCR."""
55
+ cap = cv2.VideoCapture(video_path)
56
+ if not cap.isOpened():
57
+ print(f"Error: Could not open video at {video_path}")
58
+ return None, None
59
+
60
+ # Get video properties
61
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
62
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
63
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
64
+
65
+ # Set default output path with UUID if not provided
66
+ if output_path is None:
67
+ output_dir = "apps/gradio_app/temp_data"
68
+ output_path = os.path.join(output_dir, f"output_{uuid4()}.mp4")
69
+
70
+ # Ensure output directory exists
71
+ os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
72
+
73
+ # Prepare output video
74
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
75
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
76
+
77
+ frames = []
78
+ all_plate_texts = []
79
+
80
+ while cap.isOpened():
81
+ ret, frame = cap.read()
82
+ if not ret:
83
+ print("End of video or error reading frame.")
84
+ break
85
+
86
+ try:
87
+ results = yolo_infer(model_path, frame)
88
+ except Exception as e:
89
+ print(f"Error during video inference: {e}")
90
+ break
91
+
92
+ frame_plate_texts = []
93
+ boxes_detected = False
94
+
95
+ for result in results:
96
+ for box in result.boxes:
97
+ boxes_detected = True
98
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
99
+ confidence = box.conf[0]
100
+
101
+ # Crop the license plate region
102
+ plate_region = frame[y1:y2, x1:x2]
103
+
104
+ # Run OCR on the cropped region
105
+ ocr_results = process_ocr(plate_region)
106
+ plate_text = ocr_results[0] if ocr_results else "No text detected"
107
+ frame_plate_texts.append(plate_text)
108
+
109
+ # Draw bounding box and OCR text on the frame
110
+ cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
111
+ label = f"{plate_text} ({confidence:.2f})"
112
+ cv2.putText(frame, label, (x1, y1 - 10),
113
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
114
+
115
+ if boxes_detected:
116
+ frames.append(frame)
117
+ all_plate_texts.append(frame_plate_texts)
118
+ else:
119
+ frames.append(frame)
120
+ all_plate_texts.append([])
121
+
122
+ out.write(frame)
123
+
124
+ cap.release()
125
+ out.release()
126
+ print(f"Saved processed video to {output_path}")
127
+
128
+ if not frames:
129
+ print("No frames processed.")
130
+ return None, None
131
+
132
+ # Convert list of frames to 4D NumPy array
133
+ video_array = np.stack(frames, axis=0)
134
+ return video_array, all_plate_texts
src/license_plate_detector_ocr/inference/yolo_infer.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ultralytics import YOLO
2
+
3
+ _model_cache = None
4
+
5
+ def load_yolo_model(model_path):
6
+ """Load and cache the YOLO model from the specified path."""
7
+ global _model_cache
8
+ if _model_cache is None:
9
+ try:
10
+ _model_cache = YOLO(model_path, verbose=False)
11
+ except Exception as e:
12
+ raise Exception(f"Error loading YOLO model: {e}")
13
+ return _model_cache
14
+
15
+ def yolo_infer(model_path, input_data):
16
+ """Perform YOLO inference on input data using the cached model."""
17
+ try:
18
+ model = load_yolo_model(model_path)
19
+ results = model(input_data, verbose=False)
20
+ return results
21
+ except Exception as e:
22
+ print(f"Error during YOLO inference: {e}")
23
+ return []
24
+
25
+ if __name__ == "__main__":
26
+ print("This module is intended for import, not direct execution.")
src/license_plate_detector_ocr/old2-infer.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import cv2
4
+ import numpy as np
5
+ from ultralytics import YOLO
6
+ from inference.paddleocr_infer import process_ocr
7
+
8
+ # Append the current directory to sys.path
9
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__))))
10
+
11
+ def is_image_file(file_path):
12
+ """Check if the file is an image based on its extension."""
13
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff'}
14
+ return os.path.splitext(file_path)[1].lower() in image_extensions
15
+
16
+ def process_image(model, image_path, output_path=None):
17
+ """Process a single image for license plate detection and OCR."""
18
+ image = cv2.imread(image_path)
19
+ if image is None:
20
+ print(f"Error: Could not load image from {image_path}")
21
+ return None, None
22
+
23
+ try:
24
+ results = model(image_path)
25
+ except Exception as e:
26
+ print(f"Error during image inference: {e}")
27
+ return None, None
28
+
29
+ plate_texts = []
30
+ for result in results:
31
+ for box in result.boxes:
32
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
33
+ confidence = box.conf[0]
34
+
35
+ # Crop the license plate region
36
+ plate_region = image[y1:y2, x1:x2]
37
+ # Run OCR on the cropped region
38
+ ocr_results = process_ocr(plate_region)
39
+ plate_text = ocr_results[0] if ocr_results else "No text detected"
40
+ plate_texts.append(plate_text)
41
+
42
+ # Draw bounding box and OCR text on the image
43
+ cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
44
+ label = f"{plate_text} ({confidence:.2f})"
45
+ cv2.putText(image, label, (x1, y1 - 10),
46
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
47
+
48
+ # Set default output path if not provided
49
+ if output_path is None:
50
+ output_path = os.path.splitext(image_path)[0] + '_output.jpg'
51
+
52
+ # Ensure output directory exists
53
+ os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
54
+ cv2.imwrite(output_path, image)
55
+ print(f"Saved processed image to {output_path}")
56
+
57
+ return image, plate_texts
58
+
59
+ def process_video(model, video_path, output_path=None):
60
+ """Process a video for license plate detection and OCR, writing text on detected boxes."""
61
+ cap = cv2.VideoCapture(video_path)
62
+ if not cap.isOpened():
63
+ print(f"Error: Could not open video at {video_path}")
64
+ return None, None
65
+
66
+ # Get video properties
67
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
68
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
69
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
70
+
71
+ # Set default output path if not provided
72
+ if output_path is None:
73
+ output_path = os.path.splitext(video_path)[0] + '_output.mp4'
74
+
75
+ # Ensure output directory exists
76
+ os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
77
+
78
+ # Prepare output video
79
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
80
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
81
+
82
+ frames = []
83
+ all_plate_texts = []
84
+
85
+ while cap.isOpened():
86
+ ret, frame = cap.read()
87
+ if not ret:
88
+ print("End of video or error reading frame.")
89
+ break
90
+
91
+ try:
92
+ results = model(frame)
93
+ except Exception as e:
94
+ print(f"Error during video inference: {e}")
95
+ break
96
+
97
+ frame_plate_texts = []
98
+ boxes_detected = False
99
+
100
+ for result in results:
101
+ for box in result.boxes:
102
+ boxes_detected = True
103
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
104
+ confidence = box.conf[0]
105
+
106
+ # Crop the license plate region
107
+ plate_region = frame[y1:y2, x1:x2]
108
+
109
+ # Run OCR on the cropped region
110
+ ocr_results = process_ocr(plate_region)
111
+ plate_text = ocr_results[0] if ocr_results else "No text detected"
112
+ frame_plate_texts.append(plate_text)
113
+
114
+ # Draw bounding box and OCR text on the frame
115
+ cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
116
+ label = f"{plate_text} ({confidence:.2f})"
117
+ cv2.putText(frame, label, (x1, y1 - 10),
118
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
119
+
120
+ if boxes_detected:
121
+ frames.append(frame)
122
+ all_plate_texts.append(frame_plate_texts)
123
+ else:
124
+ # Append frame even if no boxes detected to maintain video continuity
125
+ frames.append(frame)
126
+ all_plate_texts.append([])
127
+
128
+ out.write(frame)
129
+
130
+ cap.release()
131
+ out.release()
132
+ print(f"Saved processed video to {output_path}")
133
+
134
+ if not frames:
135
+ print("No frames processed.")
136
+ return None, None
137
+
138
+ # Convert list of frames to 4D NumPy array
139
+ video_array = np.stack(frames, axis=0)
140
+ return video_array, all_plate_texts
141
+
142
+ def infer(input_path, output_path=None):
143
+ """Main function to process either an image or video for license plate detection and OCR."""
144
+ model_path = "ckpts/yolo/finetune/runs/license_plate_detector/weights/best.pt"
145
+
146
+ if not os.path.exists(model_path):
147
+ print(f"Error: Model file not found at {model_path}")
148
+ return None, None
149
+
150
+ if not os.path.exists(input_path):
151
+ print(f"Error: Input file not found at {input_path}")
152
+ return None, None
153
+
154
+ try:
155
+ model = YOLO(model_path)
156
+ except Exception as e:
157
+ print(f"Error loading model: {e}")
158
+ return None, None
159
+
160
+ if is_image_file(input_path):
161
+ result_array, plate_texts = process_image(model, input_path, output_path)
162
+ else:
163
+ result_array, plate_texts = process_video(model, video_path=input_path, output_path=output_path)
164
+
165
+ return result_array, plate_texts
166
+
167
+ if __name__ == "__main__":
168
+ import argparse
169
+ parser = argparse.ArgumentParser(description="Detect and read license plates in an image or video.")
170
+ parser.add_argument("--input_path", type=str, required=True, help="Path to the input image or video file")
171
+ parser.add_argument("--output_path", type=str, default=None, help="Path to save the output file (optional)")
172
+ args = parser.parse_args()
173
+ result_array, plate_texts = infer(args.input_path, args.output_path)
src/license_plate_detector_ocr/old3-infer.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import shutil
4
+ from inference.image_video_processor import process_image, process_video
5
+
6
+ # Append the current directory to sys.path
7
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__))))
8
+
9
+ def is_image_file(file_path):
10
+ """Check if the file is an image based on its extension."""
11
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff'}
12
+ return os.path.splitext(file_path)[1].lower() in image_extensions
13
+
14
+ def clear_temp_directory(temp_dir="apps/gradio_app/temp_data"):
15
+ """Remove all files in the specified temporary directory."""
16
+ if os.path.exists(temp_dir):
17
+ try:
18
+ for filename in os.listdir(temp_dir):
19
+ file_path = os.path.join(temp_dir, filename)
20
+ if os.path.isfile(file_path) or os.path.islink(file_path):
21
+ os.unlink(file_path)
22
+ elif os.path.isdir(file_path):
23
+ shutil.rmtree(file_path)
24
+ print(f"Cleared temporary directory: {temp_dir}")
25
+ except Exception as e:
26
+ print(f"Error clearing temporary directory {temp_dir}: {e}")
27
+
28
+ def infer(input_path, output_path=None):
29
+ """Process an image or video for license plate detection and OCR."""
30
+ model_path = "ckpts/yolo/finetune/runs/license_plate_detector/weights/best.pt"
31
+
32
+ if not os.path.exists(model_path):
33
+ print(f"Error: Model file not found at {model_path}")
34
+ return None, None
35
+
36
+ if not os.path.exists(input_path):
37
+ print(f"Error: Input file not found at {input_path}")
38
+ return None, None
39
+
40
+ # Clear temporary directory before new inference
41
+ clear_temp_directory()
42
+
43
+ if is_image_file(input_path):
44
+ result_array, plate_texts = process_image(model_path, input_path, output_path)
45
+ else:
46
+ result_array, plate_texts = process_video(model_path, input_path, output_path)
47
+
48
+ return result_array, plate_texts
49
+
50
+ if __name__ == "__main__":
51
+ import argparse
52
+ parser = argparse.ArgumentParser(description="Detect and read license plates in an image or video.")
53
+ parser.add_argument("--input_path", type=str, required=True, help="Path to the input image or video file")
54
+ parser.add_argument("--output_path", type=str, default=None, help="Path to save the output file (optional)")
55
+ args = parser.parse_args()
56
+ result_array, plate_texts = infer(args.input_path, args.output_path)