Skip to content

Commit 4bffa0c

Browse files
authored
Merge pull request #3119 from JdeRobot/new_digit_classifier_exercise
[New Exercise] New Digit Classification Exercise using Deep Learning
2 parents 5c140f6 + 4b6470c commit 4bffa0c

File tree

166 files changed

+519
-5305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

166 files changed

+519
-5305
lines changed

database/exercises/db.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ COPY public.exercises (id, exercise_id, name, description, tags, status, templat
100100
14 laser_mapping Laser Mapping Build a map based on sensor readings {"tags": "ROS2"} ACTIVE RoboticsAcademy/exercises/static/exercises/laser_mapping/python_template/
101101
15 basic_computer_vision Basic Computer Vision Basic Computer Vision exercise using React and RAM {"tags": "ROS2"} ACTIVE RoboticsAcademy/exercises/static/exercises/basic_computer_vision/python_template/
102102
16 follow_road Follow Road Follow Road exercise {"tags": "ROS2"} ACTIVE RoboticsAcademy/exercises/static/exercises/follow_road/python_template/
103+
<<<<<<< new_digit_classifier_exercise
104+
17 pick_place Pick and Place Pick and Place exercise {"tags": "ROS2"} PROTOTYPE RoboticsAcademy/exercises/static/exercises/pick_place/python_template/
105+
18 digit_classification Digit Classification Deep learning-based Digit Classification. {"tags": ["Computer Vision","Deep Learning","Classification"]} PROTOTYPE RoboticsAcademy/exercises/static/exercises/digit_classification/python_template/
106+
=======
103107
17 pick_place Pick and Place Pick and Place exercise {"tags": "ROS2"} ACTIVE RoboticsAcademy/exercises/static/exercises/pick_place/python_template/
108+
>>>>>>> humble-devel
104109
\.
105110
-- 16 rescue_people_classic Rescue People Old Rescue People exercise With Gazebo Classic {"tags": "ROS2"} ACTIVE RoboticsAcademy/exercises/static/exercises/rescue_people_classic/python_template/
106111

Loading

exercises/static/exercises/basic_computer_vision/python_template/ros2_humble/GUI.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
import numpy as np
99

1010
# Graphical User Interface Class
11-
12-
1311
class GUI(MeasuringThreadingGUI):
14-
1512
def __init__(self, host="ws://127.0.0.1:2303"):
1613
super().__init__(host)
1714

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import json
2+
import cv2
3+
import base64
4+
import threading
5+
6+
from gui_interfaces.general.measuring_threading_gui_no_sim import MeasuringThreadingGUI
7+
from console_interfaces.general.console import start_console
8+
import numpy as np
9+
10+
# Graphical User Interface Class
11+
class GUI(MeasuringThreadingGUI):
12+
def __init__(self, host="ws://127.0.0.1:2303"):
13+
super().__init__(host)
14+
15+
# Execution control vars
16+
self.image_to_be_shown = None
17+
self.image_to_be_shown_updated = False
18+
self.image_show_lock = threading.Lock()
19+
20+
self.payload = {"image": "", "shape": ""}
21+
self.frame_rgb = None
22+
self.frame_rgb_lock = threading.Lock()
23+
24+
self.has_received_img = False
25+
26+
self.start()
27+
28+
# Process incoming messages to the GUI
29+
def gui_in_thread(self, ws, message):
30+
# time frame size
31+
time_frame_size = 20
32+
33+
if "ack" in message:
34+
with self.ack_lock:
35+
self.ack = True
36+
self.ack_frontend = True
37+
38+
if "pick" in message:
39+
self.has_received_img = True
40+
41+
# Image from the frontend
42+
base64_buffer = message[4:-time_frame_size]
43+
time = message[-time_frame_size:]
44+
45+
if base64_buffer.startswith("data:image/jpeg;base64,"):
46+
base64_buffer = base64_buffer[len("data:image/jpeg;base64,") :]
47+
48+
# Decodificar la cadena base64 a bytes
49+
image_data = base64.b64decode(base64_buffer)
50+
51+
# Convertir los bytes a un array de numpy
52+
nparr = np.frombuffer(image_data, np.uint8)
53+
54+
# Decodificar la imagen (convertirla a formato OpenCV)
55+
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
56+
57+
with self.frame_rgb_lock:
58+
self.frame_rgb = img
59+
ack_message = {"ack_img": "ack", "time": time}
60+
self.send_to_client(json.dumps(ack_message))
61+
62+
if "introspection" in message:
63+
info = message[len("introspection:") :]
64+
self.fps, self.lat = info.split("/")
65+
66+
# Prepares and sends a map to the websocket server
67+
def update_gui(self):
68+
69+
payload = self.payloadImage()
70+
self.payload["image"] = json.dumps(payload)
71+
72+
message = json.dumps(self.payload)
73+
self.send_to_client(message)
74+
75+
if not self.has_received_img:
76+
ack_message = {"ack_img": "ack", "time": ""}
77+
self.send_to_client(json.dumps(ack_message))
78+
79+
# Function to prepare image payload
80+
# Encodes the image as a JSON string and sends through the WS
81+
def payloadImage(self):
82+
with self.image_show_lock:
83+
image_to_be_shown_updated = self.image_to_be_shown_updated
84+
image_to_be_shown = self.image_to_be_shown
85+
86+
image = image_to_be_shown
87+
payload = {"image": "", "shape": ""}
88+
89+
if not image_to_be_shown_updated:
90+
return payload
91+
92+
shape = image.shape
93+
frame = cv2.imencode(".JPEG", image)[1]
94+
encoded_image = base64.b64encode(frame)
95+
96+
payload["image"] = encoded_image.decode("utf-8")
97+
payload["shape"] = shape
98+
99+
with self.image_show_lock:
100+
self.image_to_be_shown_updated = False
101+
102+
return payload
103+
104+
# Function for student to call
105+
def showImage(self, image):
106+
with self.image_show_lock:
107+
self.image_to_be_shown = image
108+
self.image_to_be_shown_updated = True
109+
110+
def getImage(self):
111+
with self.frame_rgb_lock:
112+
return self.frame_rgb
113+
114+
115+
host = "ws://127.0.0.1:2303"
116+
gui = GUI(host)
117+
118+
# Redirect the console
119+
start_console()
120+
121+
122+
# Expose the user functions
123+
def showImage(image):
124+
gui.showImage(image)
125+
126+
127+
def getImage():
128+
return gui.getImage()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
model_dir_path = "/workspace/code/model.onnx"
2+
3+
# This file is part of the Human Detection ROS2 package.
4+
def model_path_func()-> str:
5+
return model_dir_path
6+
7+
model_path = model_path_func()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as React from "react";
2+
import {Fragment} from "react";
3+
4+
import "./css/BasicComputerVisionRR.css";
5+
6+
const BasicComputerVisionRR = (props) => {
7+
return (
8+
<Fragment>
9+
{props.children}
10+
</Fragment>
11+
);
12+
};
13+
14+
export default BasicComputerVisionRR;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as React from "react";
2+
import noImage from "../../assets/img/noImage.png";
3+
4+
import "./css/GUICanvas.css"
5+
6+
function SpecificBasicComputerVision(props) {
7+
React.useEffect(() => {
8+
console.log("TestShowScreen subscribing to ['update'] events");
9+
const callback = (message) => {
10+
11+
if (message.data.update.image) {
12+
var canvas = document.getElementById("gui_canvas");
13+
14+
// Request Animation Frame to remove the flickers
15+
function decode_utf8(s) {
16+
return decodeURIComponent(escape(s))
17+
}
18+
19+
// Parse the Image Data
20+
var image_data = JSON.parse(message.data.update.image),
21+
source = decode_utf8(image_data.image),
22+
shape = image_data.shape;
23+
24+
if (source != "" && shape instanceof Array) {
25+
canvas.src = "data:image/jpeg;base64," + source;
26+
canvas.width = shape[1];
27+
canvas.height = shape[0];
28+
}
29+
}
30+
31+
// Send the ACK of the msg
32+
window.RoboticsExerciseComponents.commsManager.send("gui", "ack");
33+
};
34+
35+
window.RoboticsExerciseComponents.commsManager.subscribe(
36+
[window.RoboticsExerciseComponents.commsManager.events.UPDATE],
37+
callback
38+
);
39+
40+
return () => {
41+
console.log("TestShowScreen unsubscribing from ['state-changed'] events");
42+
window.RoboticsExerciseComponents.commsManager.unsubscribe(
43+
[window.RoboticsExerciseComponents.commsManager.events.UPDATE],
44+
callback
45+
);
46+
};
47+
}, []);
48+
49+
return (
50+
<div style={{display: "flex", width: "100%", height: "100%", position:"relative", justifyContent: "center"}}>
51+
<img className="image" id="gui_canvas" src={noImage}/>
52+
</div>
53+
);
54+
}
55+
56+
export default SpecificBasicComputerVision;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
* {
2+
box-sizing: border-box;
3+
}
4+
5+
body, html {
6+
width: 100%; height: 100%;
7+
}
8+
9+
#exercise-container {
10+
position: absolute;
11+
top: 0; left: 0; bottom: 0; right: 0;
12+
overflow: hidden;
13+
display: flex;
14+
flex-direction: column;
15+
}
16+
17+
#exercise-container #content {
18+
width: 100%;
19+
height: 100%;
20+
overflow: hidden;
21+
}
22+
23+
#exercise-container #content #content-exercise {
24+
display: flex;
25+
flex-direction: column;
26+
width: 100%;
27+
height: 100%;
28+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.image {
2+
position: absolute;
3+
width: fit-content;
4+
height: 100%;
5+
}

exercises/static/exercises/dl_digit_classifier/README.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

exercises/static/exercises/dl_digit_classifier/console.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)