bahakizil commited on
Commit
bd06921
·
verified ·
1 Parent(s): c90c237

Upload 7 files

Browse files
Files changed (7) hide show
  1. app.py +197 -0
  2. cctv_agent.py +68 -0
  3. deepseek_agent.py +126 -0
  4. last.pt +3 -0
  5. mail_agent.py +64 -0
  6. requirements.txt +8 -0
  7. yolo_agent.py +160 -0
app.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import gradio as gr
4
+
5
+ from yolo_agent import video_detection_tool
6
+ from deepseek_agent import analysis_tool
7
+ from mail_agent import mail_tool
8
+ from cctv_agent import cctv_detection_tool
9
+
10
+ def cli_pipeline():
11
+ """
12
+
13
+ """
14
+ video_path = input("Enter the full path to the video file: ").strip()
15
+ if not os.path.exists(video_path):
16
+ print("The specified file was not found!")
17
+ return
18
+
19
+ # 1. YOLO Detection
20
+ with open(video_path, 'rb') as video_file:
21
+ detection_result = video_detection_tool({"video": video_file})
22
+ print("Detection Result:\n", detection_result)
23
+
24
+ # 2. DeepSeek Analysis
25
+ detections_folder = "detections"
26
+ detections_file = os.path.join(detections_folder, "detections.txt")
27
+ if not os.path.exists(detections_file):
28
+ print("Detections file not found! Please check the detection process first.")
29
+ return
30
+
31
+ analysis_result = analysis_tool(detections_file)
32
+ print("Analysis Result:\n", analysis_result)
33
+
34
+ # 3. Email Sending (optional)
35
+ analysis_file = os.path.join(detections_folder, "analysis.txt")
36
+ if not os.path.exists(analysis_file):
37
+ print("analysis.txt not found; email cannot be sent.")
38
+ return
39
+
40
+ image_path = os.path.join(detections_folder, "1.png")
41
+ if not os.path.exists(image_path):
42
+ print("1.png not found; email will not be sent.")
43
+ return
44
+
45
+ sender_email = input("Enter the sender email address: ").strip()
46
+ sender_password = input("Enter the email application password: ").strip()
47
+ recipient_email = input("Enter the recipient email address: ").strip()
48
+
49
+ mail_result = mail_tool({
50
+ "input_data": {
51
+ "analysis_file": analysis_file,
52
+ "image_file": image_path,
53
+ "sender_email": sender_email,
54
+ "sender_password": sender_password,
55
+ "recipient_email": recipient_email
56
+ }
57
+ })
58
+ print("Email Result:\n", mail_result)
59
+
60
+
61
+ def process_input(mode, video_file, rtsp_url, model_selection, user_id, sender_email, sender_password, recipient_email):
62
+ """
63
+ Processes inputs from the Gradio interface:
64
+ - In "Upload Video" mode: runs YOLO detection via video_detection_tool.
65
+ - In "Connect to CCTV" mode: runs detection via cctv_detection_tool using the provided RTSP URL.
66
+
67
+ In video mode, if detection and analysis are successful, it uses the email settings
68
+ provided by the user to send an email.
69
+ """
70
+ if mode == "Upload Video":
71
+ if video_file is None:
72
+ return "Please upload a video file."
73
+ detection_result = video_detection_tool({"video": video_file})
74
+ detections_file = os.path.join("detections", "detections.txt")
75
+ if not os.path.exists(detections_file):
76
+ return detection_result + "\n\nDetections file not found."
77
+ analysis_result = analysis_tool(detections_file)
78
+ image_path = os.path.join("detections", "1.png")
79
+ if os.path.exists(image_path):
80
+ if not sender_email or not sender_password or not recipient_email:
81
+ mail_part = "Missing email information for sending the email."
82
+ else:
83
+ mail_part = mail_tool({
84
+ "input_data": {
85
+ "analysis_file": os.path.join("detections", "analysis.txt"),
86
+ "image_file": image_path,
87
+ "sender_email": sender_email,
88
+ "sender_password": sender_password,
89
+ "recipient_email": recipient_email
90
+ }
91
+ })
92
+ return f"{detection_result}\n\n{analysis_result}\n\n{mail_part}"
93
+ else:
94
+ return f"{detection_result}\n\n{analysis_result}\n\n1.png not found, email not sent."
95
+
96
+ elif mode == "Connect to CCTV":
97
+ if not rtsp_url or rtsp_url.strip() == "":
98
+ return "Please enter a valid RTSP URL."
99
+ detection_result = cctv_detection_tool(rtsp_url, model_selection)
100
+ return detection_result
101
+
102
+ else:
103
+ return "Invalid mode selected."
104
+
105
+
106
+ def launch_gradio_interface():
107
+ """
108
+ Launches the Gradio interface.
109
+ Users can choose either "Upload Video" or "Connect to CCTV" mode and provide the necessary inputs
110
+ to perform detection, analysis, and optionally email sending.
111
+ """
112
+ mode_choices = ["Upload Video", "Connect to CCTV"]
113
+ model_choices = ["last.pt"]
114
+
115
+ with gr.Blocks(css=".gradio-container { font-family: 'Segoe UI', sans-serif; }") as demo:
116
+ gr.Markdown("## YOLO Detection: Video or CCTV Stream\n"
117
+ "In this interface, you can either upload your local video or connect via a CCTV RTSP stream "
118
+ "to perform object detection.\n"
119
+ "**Additionally:** DeepSeek analysis and email sending are integrated.")
120
+ gr.Markdown("### 1) Mode Selection")
121
+ mode = gr.Radio(mode_choices, label="How would you like to perform the detection?", value="Upload Video")
122
+ gr.Markdown("---")
123
+
124
+ with gr.Row():
125
+ with gr.Column():
126
+ gr.Markdown("**Local Video File**")
127
+ video_input = gr.Video(
128
+ label="Upload Video",
129
+ sources="upload",
130
+ visible=True
131
+ )
132
+ with gr.Column():
133
+ gr.Markdown("**CCTV Stream (RTSP)**")
134
+ rtsp_input = gr.Textbox(
135
+ label="RTSP URL",
136
+ placeholder="rtsp://username:password@ip:port/stream",
137
+ visible=False
138
+ )
139
+ gr.Markdown("---")
140
+ with gr.Row():
141
+ model_selection_input = gr.Dropdown(
142
+ model_choices,
143
+ label="YOLO Model Selection",
144
+ value="last.pt",
145
+ info="Select the YOLO model you wish to use."
146
+ )
147
+ user_id = gr.Textbox(
148
+ label="User ID (Optional)",
149
+ placeholder="Enter your user ID..."
150
+ )
151
+ gr.Markdown("### Email Information (for sending results)")
152
+ with gr.Row():
153
+ sender_email = gr.Textbox(
154
+ label="Sender Email Address",
155
+ placeholder="e.g., [email protected]"
156
+ )
157
+ sender_password = gr.Textbox(
158
+ label="Email (app) Password",
159
+ placeholder="Enter your password",
160
+ type="password"
161
+ )
162
+ recipient_email = gr.Textbox(
163
+ label="Recipient Email Address",
164
+ placeholder="e.g., [email protected]"
165
+ )
166
+ gr.Markdown("---")
167
+ output_text = gr.Textbox(
168
+ label="Output",
169
+ interactive=False,
170
+ placeholder="Detection, analysis, and email output will be shown here..."
171
+ )
172
+
173
+ def update_visibility(selected_mode):
174
+ if selected_mode == "Upload Video":
175
+ return gr.update(visible=True), gr.update(visible=False)
176
+ else:
177
+ return gr.update(visible=False), gr.update(visible=True)
178
+
179
+ mode.change(fn=update_visibility, inputs=mode, outputs=[video_input, rtsp_input])
180
+ submit_btn = gr.Button("Start Detection", variant="primary")
181
+ submit_btn.click(fn=process_input,
182
+ inputs=[mode, video_input, rtsp_input, model_selection_input, user_id, sender_email, sender_password, recipient_email],
183
+ outputs=output_text)
184
+ gr.Markdown("---\n"
185
+ "bahakizil\n")
186
+ demo.launch(server_name="0.0.0.0", server_port=7860)
187
+
188
+
189
+ def main():
190
+ cli_pipeline()
191
+
192
+
193
+ if __name__ == "__main__":
194
+ if len(sys.argv) > 1 and sys.argv[1] == "--cli":
195
+ main()
196
+ else:
197
+ launch_gradio_interface()
cctv_agent.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### START OF cctv_agent.py ###
2
+ import os
3
+ import cv2
4
+ from ultralytics import YOLO
5
+ from langchain.agents import tool
6
+
7
+ @tool("cctv_detection_tool", return_direct=True)
8
+ def cctv_detection_tool(rtsp_url: str, model_path: str = None, output_dir: str = "detections", frame_skip: int = 30, conf: float = 0.75) -> str:
9
+ """
10
+ RTSP URL'si verilen CCTV kamerasına bağlanarak YOLO ile canlı nesne tespiti yapar.
11
+
12
+ Args:
13
+ rtsp_url (str): CCTV RTSP bağlantı URL'si.
14
+ model_path (str, optional): YOLO model dosyasının yolu. Belirtilmezse yolo_agent.py içindeki default (last.pt) kullanılır.
15
+ output_dir (str): Tespit çıktı dosyalarının kaydedileceği dizin.
16
+ frame_skip (int): İşlenecek kareler arasında atlanacak kare sayısı.
17
+ conf (float): Tespit için güven eşiği.
18
+
19
+ Returns:
20
+ str: İşlemin sonucuna dair özet mesaj.
21
+ """
22
+ if model_path is None:
23
+ model_path = os.path.join(os.path.dirname(__file__), "yolo", "last.pt")
24
+ cap = cv2.VideoCapture(rtsp_url)
25
+ if not cap.isOpened():
26
+ return f"RTSP akışı açılamadı: {rtsp_url}"
27
+ os.makedirs(output_dir, exist_ok=True)
28
+ output_txt = os.path.join(output_dir, "cctv_detections.txt")
29
+ ftxt = open(output_txt, "w")
30
+ try:
31
+ model = YOLO(model_path)
32
+ except Exception as e:
33
+ return f"YOLO modeli yüklenemedi: {e}"
34
+ frame_count = 0
35
+ saved_image_index = 1
36
+ while cap.isOpened():
37
+ ret, frame = cap.read()
38
+ if not ret:
39
+ break
40
+ results = model(frame, conf=conf)
41
+ detections = results[0].boxes.data.cpu().numpy() if len(results) > 0 else []
42
+ valid_detections = [det for det in detections if int(det[5]) in [0, 1, 2, 3, 4, 5]]
43
+ if valid_detections:
44
+ for det in valid_detections:
45
+ x1, y1, x2, y2, conf_score, cls_ = det
46
+ class_id = int(cls_)
47
+ if class_id in [0, 1, 2]:
48
+ class_label = "Danger"
49
+ elif class_id in [3, 4, 5]:
50
+ class_label = model.names.get(class_id, f"Class {class_id}")
51
+ else:
52
+ class_label = f"Class {class_id}"
53
+ cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 3)
54
+ ftxt.write(f"Frame {frame_count}: {class_label} at ({int(x1)}, {int(y1)}, {int(x2)}, {int(y2)})\n")
55
+ # Örnek olarak bir kareyi kaydet
56
+ output_frame_path = os.path.join(output_dir, f"cctv_{saved_image_index}.png")
57
+ cv2.imwrite(output_frame_path, frame)
58
+ saved_image_index += 1
59
+ frame_count += frame_skip
60
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_count)
61
+ # Demo amacıyla belirli sayıda kare işlendikten sonra döngüden çıkıyoruz
62
+ if frame_count > 300:
63
+ break
64
+ ftxt.close()
65
+ cap.release()
66
+ cv2.destroyAllWindows()
67
+ return f"CCTV tespiti tamamlandı. Çıktılar '{output_dir}' dizininde; log: '{output_txt}'."
68
+ ### END OF cctv_agent.py ###
deepseek_agent.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from langchain.tools import tool
4
+ from langchain_ollama import ChatOllama
5
+
6
+ @tool("analysis_tool", return_direct=True)
7
+ def analysis_tool(detections_file: str) -> str:
8
+ """
9
+ Reads the YOLO detection log (detections_file) and uses the DeepSeek model via ChatOllama
10
+ to produce an email-style summary in plain text with:
11
+
12
+ - Number of 'Danger' detections
13
+ - Brief explanation
14
+ - General comment/warning
15
+
16
+ The final text is written to 'analysis.txt', no chain-of-thought or placeholders.
17
+ """
18
+ if not os.path.exists(detections_file):
19
+ return f"detections_file not found: {detections_file}"
20
+
21
+ # Read YOLO detection log
22
+ with open(detections_file, "r") as f:
23
+ detections_text = f.read().strip()
24
+
25
+ # Count Danger occurrences
26
+ manual_count = detections_text.count("Danger")
27
+
28
+ # Build system and human prompts
29
+ system_prompt = (
30
+ "You are an AI that summarizes YOLO detection logs. "
31
+ "Return only the final summary with 3 bullet lines:\n"
32
+ "1) - Number of 'Danger' detections: X\n"
33
+ "2) - Brief explanation: ...\n"
34
+ "3) - General comment/warning: ...\n"
35
+ "No chain of thought, no subject, no email headers. Output ONLY the body lines in plain text."
36
+ )
37
+
38
+ human_prompt = f"""
39
+ YOLO detection log:
40
+ {detections_text}
41
+
42
+ Please produce exactly 3 bullet lines:
43
+ - Number of 'Danger' detections: <number>
44
+ - Brief explanation: <one line summary>
45
+ - General comment/warning: <safety note>
46
+ """
47
+
48
+ llm = ChatOllama(
49
+ model="deepseek-r1:1.5b",
50
+ temperature=0.0,
51
+ base_url="http://localhost:11434",
52
+ )
53
+
54
+ messages = [
55
+ ("system", system_prompt),
56
+ ("human", human_prompt),
57
+ ]
58
+
59
+ # Get raw model output
60
+ ai_msg = llm.invoke(messages)
61
+ raw_output = ai_msg.content.strip()
62
+
63
+ # Remove <think> blocks
64
+ raw_output = re.sub(r"<think>.*?</think>", "", raw_output, flags=re.DOTALL)
65
+
66
+ # Split lines
67
+ lines = [line.strip() for line in raw_output.splitlines() if line.strip()]
68
+
69
+ # We'll look for the bullet lines
70
+ danger_idx = None
71
+ explain_idx = None
72
+ comment_idx = None
73
+
74
+ for i, line in enumerate(lines):
75
+ lower_line = line.lower()
76
+ if "number of 'danger' detections:" in lower_line:
77
+ danger_idx = i
78
+ elif "brief explanation:" in lower_line:
79
+ explain_idx = i
80
+ elif "general comment/warning:" in lower_line:
81
+ comment_idx = i
82
+
83
+ final_lines = lines[:]
84
+
85
+ # 1) If Danger line doesn't exist, append fallback
86
+ if danger_idx is None:
87
+ final_lines.append(f"- Number of 'Danger' detections: {manual_count}")
88
+ else:
89
+ # If it exists but has placeholder <number>, replace it
90
+ line_text = final_lines[danger_idx]
91
+ if "<number>" in line_text:
92
+ new_line = line_text.replace("<number>", str(manual_count))
93
+ final_lines[danger_idx] = new_line
94
+
95
+ # 2) Explanation line fallback
96
+ if explain_idx is None:
97
+ if manual_count == 0:
98
+ final_lines.append("- Brief explanation: No Danger found.")
99
+ else:
100
+ final_lines.append("- Brief explanation: Potential risks detected.")
101
+ else:
102
+ # If it has <one line summary> placeholder
103
+ if "<one line summary>" in final_lines[explain_idx]:
104
+ final_lines[explain_idx] = final_lines[explain_idx].replace(
105
+ "<one line summary>",
106
+ "Potential risks detected." if manual_count else "No Danger found."
107
+ )
108
+
109
+ # 3) Comment line fallback
110
+ if comment_idx is None:
111
+ final_lines.append("- General comment/warning: Review and investigate for safety issues.")
112
+ else:
113
+ if "<safety note>" in final_lines[comment_idx]:
114
+ final_lines[comment_idx] = final_lines[comment_idx].replace(
115
+ "<safety note>", "Please monitor closely." # or custom
116
+ )
117
+
118
+ # Join final lines
119
+ final_output = "\n".join(final_lines)
120
+
121
+ # Save to analysis.txt
122
+ analysis_path = os.path.join(os.path.dirname(detections_file), "analysis.txt")
123
+ with open(analysis_path, "w") as out_f:
124
+ out_f.write(final_output)
125
+
126
+ return f"Analysis complete. Summary saved to '{analysis_path}'."
last.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3c7f69ad83e71c4a19ffbcdff30fcabbbbee4ef335033c8fb24b35186d0f38ce
3
+ size 5470931
mail_agent.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.tools import tool
2
+ import os
3
+ import smtplib
4
+ from email.mime.text import MIMEText
5
+ from email.mime.multipart import MIMEMultipart
6
+ from email.mime.base import MIMEBase
7
+ from email import encoders
8
+
9
+ @tool("mail_tool", return_direct=True)
10
+ def mail_tool(input_data: dict) -> str:
11
+ """
12
+ Expects a dict parameter with the following keys:
13
+ - analysis_file: Path to the analysis.txt file.
14
+ - image_file: Path to an image file (e.g., 1.png) to attach.
15
+ - sender_email: The email address from which to send the email.
16
+ - sender_password: The password or app-specific password for the sender email.
17
+ - recipient_email: The email address to send the email to.
18
+
19
+ This function reads the analysis report, attaches the image, and sends an email using the provided credentials.
20
+ """
21
+ analysis_file = input_data.get("analysis_file", "")
22
+ image_file = input_data.get("image_file", "")
23
+ sender_email = input_data.get("sender_email", "")
24
+ sender_password = input_data.get("sender_password", "")
25
+ recipient_email = input_data.get("recipient_email", "")
26
+
27
+ if not os.path.exists(analysis_file):
28
+ return f"Analysis file not found: {analysis_file}"
29
+
30
+ with open(analysis_file, "r") as f:
31
+ email_body = f.read().strip()
32
+
33
+ SMTP_SERVER = "smtp.gmail.com"
34
+ SMTP_PORT = 587
35
+
36
+ msg = MIMEMultipart()
37
+ msg["Subject"] = "DeepSeek Analysis Report"
38
+ msg["From"] = sender_email
39
+ msg["To"] = recipient_email
40
+ msg.attach(MIMEText(email_body, "plain"))
41
+
42
+ if image_file and os.path.isfile(image_file):
43
+ try:
44
+ with open(image_file, "rb") as attachment:
45
+ part = MIMEBase("application", "octet-stream")
46
+ part.set_payload(attachment.read())
47
+ encoders.encode_base64(part)
48
+ part.add_header("Content-Disposition", f'attachment; filename={os.path.basename(image_file)}')
49
+ msg.attach(part)
50
+ except Exception as e:
51
+ return f"Error attaching {image_file}: {e}"
52
+ else:
53
+ return f"No image file found at '{image_file}'. Email not sent."
54
+
55
+ try:
56
+ with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
57
+ server.ehlo()
58
+ server.starttls()
59
+ server.login(sender_email, sender_password)
60
+ server.send_message(msg)
61
+ except Exception as e:
62
+ return f"Failed to send email: {str(e)}"
63
+
64
+ return f"Email sent successfully to {recipient_email}."
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ langchain_ollama
3
+ ultralytics
4
+ opencv-python
5
+ transformers
6
+ accelerate
7
+ torch
8
+ torchvision
yolo_agent.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LangChain YOLO Agent
3
+ ---------------------
4
+
5
+ This project provides a YOLO-based object detection tool integrated with LangChain.
6
+ Users can upload any video to analyze its contents, generate object detection logs,
7
+ and visualize detections with bounding boxes.
8
+
9
+ Steps:
10
+ 1) Install dependencies: `pip install langchain openai ultralytics opencv-python`
11
+ 2) Add this file (`yolo_agent.py`) to your project.
12
+ 3) Ensure that the YOLO model file (`last.pt`) is available in the working directory.
13
+ 4) Use the provided functions to analyze uploaded videos dynamically.
14
+ """
15
+
16
+ import os
17
+ import cv2
18
+ import shutil
19
+ from langchain.agents import Tool, tool
20
+ from ultralytics import YOLO
21
+
22
+ UPLOAD_FOLDER = "uploads"
23
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
24
+
25
+ MODEL_PATH = os.path.join(os.path.dirname(__file__), "last.pt")
26
+
27
+ def detect_with_yolo(
28
+ video_path: str,
29
+ model_path: str = MODEL_PATH,
30
+ output_dir: str = "detections",
31
+ frame_skip: int = 7,
32
+ conf: float = 0.75,
33
+ ) -> str:
34
+ """
35
+ Runs YOLO detection on the given video.
36
+ - Detects only class_id 0..5 (Danger / Handgun / Knife, etc.)
37
+ - Draws red bounding boxes
38
+ - Saves logs to a text file
39
+ - Saves detected frames as sequential PNG images (1.png, 2.png, 3.png, ...)
40
+ """
41
+
42
+ if not os.path.exists(video_path):
43
+ return f"Video not found: {video_path}"
44
+
45
+ try:
46
+ model = YOLO(model_path)
47
+ except Exception as e:
48
+ return f"Failed to load model: {e}"
49
+
50
+ cap = cv2.VideoCapture(video_path)
51
+ if not cap.isOpened():
52
+ return f"Cannot open video: {video_path}"
53
+
54
+ os.makedirs(output_dir, exist_ok=True)
55
+ output_txt = os.path.join(output_dir, "detections.txt")
56
+
57
+ frame_count = 0
58
+ saved_image_index = 1 # Sıralı kaydetmek için sayaç
59
+
60
+ with open(output_txt, "w") as ftxt:
61
+ while cap.isOpened():
62
+ ret, frame = cap.read()
63
+ if not ret:
64
+ break
65
+
66
+ results = model(frame, conf=conf)
67
+ detections = (
68
+ results[0].boxes.data.cpu().numpy() if len(results) > 0 else []
69
+ )
70
+
71
+ valid_detections = [det for det in detections if int(det[5]) in [0, 1, 2, 3, 4, 5]]
72
+
73
+ if len(valid_detections) > 0:
74
+ for det in valid_detections:
75
+ x1, y1, x2, y2, conf_score, cls_ = det
76
+ class_id = int(cls_)
77
+
78
+ if class_id in [0, 1, 2]:
79
+ class_label = "Danger"
80
+ elif class_id in [3, 4, 5]:
81
+ # Alternatif class isimleri
82
+ class_label = model.names.get(class_id, f"Class {class_id}")
83
+ else:
84
+ class_label = f"Class {class_id}"
85
+
86
+ cv2.rectangle(
87
+ frame,
88
+ (int(x1), int(y1)),
89
+ (int(x2), int(y2)),
90
+ (0, 0, 255),
91
+ 3,
92
+ )
93
+
94
+ (w, h), _ = cv2.getTextSize(class_label, cv2.FONT_HERSHEY_COMPLEX, 0.8, 2)
95
+ label_x1 = int(x1)
96
+ label_y2 = int(y1)
97
+ label_y1 = label_y2 - h - 10
98
+ label_x2 = label_x1 + w + 10
99
+
100
+ cv2.rectangle(
101
+ frame,
102
+ (label_x1, label_y1),
103
+ (label_x2, label_y2),
104
+ (0, 0, 255),
105
+ cv2.FILLED,
106
+ )
107
+
108
+ cv2.putText(
109
+ frame,
110
+ class_label,
111
+ (label_x1 + 5, label_y1 + h + 5),
112
+ cv2.FONT_HERSHEY_COMPLEX,
113
+ 0.85,
114
+ (255, 255, 255),
115
+ 2,
116
+ cv2.LINE_AA,
117
+ )
118
+
119
+ ftxt.write(
120
+ f"Frame {frame_count}: {class_label} at ({int(x1)}, {int(y1)}, {int(x2)}, {int(y2)})\n"
121
+ )
122
+
123
+ # Kaydedilecek resim ismi -> 1.png, 2.png, 3.png
124
+ output_frame_path = os.path.join(output_dir, f"{saved_image_index}.png")
125
+ cv2.imwrite(output_frame_path, frame)
126
+ saved_image_index += 1
127
+
128
+ frame_count += frame_skip
129
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_count)
130
+
131
+ cap.release()
132
+ cv2.destroyAllWindows()
133
+
134
+ return f"Processing complete. Outputs saved in '{output_dir}' and '{output_txt}'."
135
+
136
+ @tool("video_detection_tool", return_direct=True)
137
+ def video_detection_tool(video) -> str:
138
+ """
139
+ Handles video uploads dynamically and runs YOLO detection.
140
+ Expects that the input 'video' is either a file-like object with a 'name' attribute
141
+ or a string representing the file path.
142
+ """
143
+ if not os.path.exists(UPLOAD_FOLDER):
144
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
145
+
146
+ # Eğer video bir dosya nesnesiyse video.name kullan, aksi halde doğrudan video string'idir.
147
+ try:
148
+ video_name = video.name
149
+ except AttributeError:
150
+ video_name = video
151
+
152
+ video_path = os.path.join(UPLOAD_FOLDER, os.path.basename(video_name))
153
+ if os.path.abspath(video_name) != os.path.abspath(video_path):
154
+ shutil.copy(video_name, video_path)
155
+ return detect_with_yolo(video_path)
156
+
157
+ if __name__ == "__main__":
158
+ print("LangChain YOLO Agent Ready!")
159
+
160
+ __all__ = ["video_detection_tool"]