Skip to content

Commit 391bc11

Browse files
committed
merging latest changes from main
2 parents abf512f + a789b91 commit 391bc11

File tree

6 files changed

+132
-51
lines changed

6 files changed

+132
-51
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ config/make_request.env
1515
config/pp_daily_test.env
1616
config/restoreunstable.env
1717
config/sendimagereq.env
18+
config/slack-webhook-preprocessors.env
1819
*.sh
1920
docker-compose.override.yml

handlers/map-tactile-svg/map-svg.py

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import time
2323
import drawSvg as draw
2424
from datetime import datetime
25+
import math
2526
from config.logging_utils import configure_logging
2627

2728
configure_logging()
@@ -115,22 +116,25 @@ def handle():
115116
return response
116117

117118
dimensions = 700, 700
119+
data = preprocessor["ca.mcgill.a11y.image.preprocessor.openstreetmap"]
120+
lat = data["bounds"]["latitude"]
121+
lon = data["bounds"]["longitude"]
122+
coords = getMidpoint(contents)
118123

119124
renderingDescription = ("Tactile rendering of map centered at latitude " +
120-
str(contents["coordinates"]["latitude"]) +
125+
str(coords["latitude"]) +
121126
" and longitude " +
122-
str(contents["coordinates"]["longitude"]))
127+
str(coords["longitude"]))
123128
caption = ("Map centered at latitude " +
124-
str(contents["coordinates"]["latitude"]) +
129+
str(coords["latitude"]) +
125130
" and longitude " +
126-
str(contents["coordinates"]["longitude"]))
131+
str(coords["longitude"]))
127132
# List of minor street types ('footway', 'crossing' and 'steps')
128133
# to be filtered out to simplify the resulting rendering
129134
remove_streets = ["footway", "crossing", "steps", "elevator"]
130135
svg = draw.Drawing(dimensions[0], dimensions[1],
131136
origin=(0, -dimensions[1]))
132137

133-
data = preprocessor["ca.mcgill.a11y.image.preprocessor.openstreetmap"]
134138
if "streets" in data:
135139
streets = data["streets"]
136140
lat = data["bounds"]["latitude"]
@@ -438,14 +442,12 @@ def getNodeCategoryData(POI):
438442
category = POI["cat"]
439443
match category:
440444
case "crossing":
441-
if POI["crossing"] == "marked":
442-
tag += "Marked crossing, "
443-
elif POI["crossing"] == "unmarked":
444-
tag += "Unmarked crossing, "
445-
elif POI["crossing"] == "traffic_signals":
446-
tag += "Crossing with traffic signal, "
447-
else:
448-
tag += "Crossing, "
445+
crossing_types = {
446+
"marked": "Marked crossing, ",
447+
"unmarked": "Unmarked crossing, ",
448+
"traffic_signals": "Crossing with traffic signal, "
449+
}
450+
tag += crossing_types.get(POI.get("crossing"), "Crossing, ")
449451
case "traffic_signals":
450452
tag += "Traffic lights present, "
451453
case _:
@@ -491,5 +493,42 @@ def health():
491493
}), 200
492494

493495

496+
def getMidpoint(contents):
497+
if "coordinates" in contents:
498+
logging.debug("Coordinates found in request")
499+
return {"latitude": contents["coordinates"]["latitude"],
500+
"longitude": contents["coordinates"]["longitude"]}
501+
502+
logging.debug("Coordinates not found in request. "
503+
"Calculating midpoint from bounds.")
504+
data = contents["preprocessors"][
505+
"ca.mcgill.a11y.image.preprocessor.openstreetmap"]
506+
lat = data["bounds"]["latitude"]
507+
lon = data["bounds"]["longitude"]
508+
509+
# Convert degrees to radians
510+
lat1, lon1, lat2, lon2 = map(math.radians,
511+
[lat["min"], lon["min"],
512+
lat["max"], lon["max"]])
513+
514+
# Convert to Cartesian coordinates
515+
x1, y1, z1 = (math.cos(lat1) * math.cos(lon1),
516+
math.cos(lat1) * math.sin(lon1), math.sin(lat1))
517+
x2, y2, z2 = (math.cos(lat2) * math.cos(lon2),
518+
math.cos(lat2) * math.sin(lon2), math.sin(lat2))
519+
520+
# Compute the midpoint in Cartesian coordinates
521+
x_m, y_m, z_m = (x1 + x2) / 2, (y1 + y2) / 2, (z1 + z2) / 2
522+
523+
# Convert back to latitude and longitude
524+
lon_m = math.atan2(y_m, x_m)
525+
hyp = math.sqrt(x_m**2 + y_m**2)
526+
lat_m = math.atan2(z_m, hyp)
527+
528+
# Convert radians back to degrees
529+
return {"latitude": round(math.degrees(lat_m), 6),
530+
"longitude": round(math.degrees(lon_m), 6)}
531+
532+
494533
if __name__ == "__main__":
495534
app.run(host="0.0.0.0", port=80, debug=True)

handlers/photo-audio-handler/src/server.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,6 @@ app.post("/handler", async (req, res) => {
135135
}
136136
}
137137

138-
// Generate rendering title
139-
const renderingTitle = utils.renderingTitle(semseg, objDet, objGroup);
140-
141138
// Handle language if targetLanguage is not English
142139
if (targetLanguage != "en") {
143140
console.debug(`Translating ttsData values to ${targetLanguage}"`);
@@ -163,7 +160,7 @@ app.post("/handler", async (req, res) => {
163160
const textString = ttsData.map(x => x["value"]).join(" ");
164161
const rendering = {
165162
"type_id": "ca.mcgill.a11y.image.renderer.Text",
166-
"description": renderingTitle + " (text only)",
163+
"description": "Text description",
167164
"data": { "text": textString }
168165
};
169166
if (ajv.validate("https://image.a11y.mcgill.ca/renderers/text.schema.json", rendering["data"])) {
@@ -237,7 +234,7 @@ app.post("/handler", async (req, res) => {
237234
console.debug("Constructing segment audio rendering")
238235
const rendering = {
239236
"type_id": "ca.mcgill.a11y.image.renderer.SegmentAudio",
240-
"description": renderingTitle,
237+
"description": "Rich audio description",
241238
"data": {
242239
"audioFile": dataURL,
243240
"audioInfo": segArray
@@ -256,7 +253,7 @@ app.post("/handler", async (req, res) => {
256253
console.debug("Constructing simple audio rendering")
257254
const rendering = {
258255
"type_id": "ca.mcgill.a11y.image.renderer.SimpleAudio",
259-
"description": renderingTitle,
256+
"description": "Rich audio description",
260257
"data": {
261258
"audio": dataURL
262259
},
@@ -291,22 +288,22 @@ app.post("/handler", async (req, res) => {
291288
}
292289

293290
// Translate renderings' description before sending response
294-
if (targetLanguage !== "en") {
295-
try {
296-
console.debug("Translating renderings description to " + targetLanguage);
297-
const translatedDesc = await utils.getTranslationSegments(
291+
if (targetLanguage !== "en") {
292+
try {
293+
console.debug("Translating renderings description to " + targetLanguage);
294+
const translatedDesc = await utils.getTranslationSegments(
298295
renderings.map(x => x["description"]),
299296
targetLanguage
300297
);
301298

302-
for (let i = 0; i < renderings.length; i++) {
303-
renderings[i]["description"] = translatedDesc[i];
304-
}
305-
} catch(e) {
306-
console.error("Failed to generate audio!");
299+
for (let i = 0; i < renderings.length; i++) {
300+
renderings[i]["description"] = translatedDesc[i];
301+
}
302+
} catch(e) {
303+
console.error("Failed to translate rendering descriptions to " + targetLanguage);
307304
piiLogger.pii(`Error: ${(e as Error).message}`);
308-
}
309-
}
305+
}
306+
}
310307
// Send response
311308

312309
const response = utils.generateEmptyResponse(req.body["request_uuid"]);

handlers/photo-audio-handler/src/utils.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export function generateActions(objs: Obj[], group: number[], actionRec: Action)
143143
const act = actionRec["actions"].find((x: { "personID": number }) => x["personID"] === id);
144144
if (act !== undefined) {
145145
const label = act["action"].trim();
146-
if (act["confidence"] < ACT_THRES) {
146+
if (act["confidence"] < ACT_THRES) {
147147
if (maybeActions[label]) {
148148
maybeActions[label].push(id);
149149
}
@@ -158,9 +158,9 @@ export function generateActions(objs: Obj[], group: number[], actionRec: Action)
158158
}
159159
else {
160160
other.push(id);
161-
}
161+
}
162162
}
163-
163+
164164
for (const label in actions) {
165165
const len = actions[label].length;
166166
const pType = len > 1 ? "people" : "person";
@@ -172,7 +172,7 @@ export function generateActions(objs: Obj[], group: number[], actionRec: Action)
172172
"label": pType + " " + actionTxt,
173173
"value": len.toString() + " " + pType + " " + actionTxt + ","
174174
};
175-
objects.push(object);
175+
objects.push(object);
176176
}
177177
for (const label in maybeActions) {
178178
const len = maybeActions[label].length;
@@ -184,7 +184,7 @@ export function generateActions(objs: Obj[], group: number[], actionRec: Action)
184184
"objects": acts,
185185
"value": len.toString() + " " + pType + " who might be " + actionTxt + ","
186186
};
187-
objects.push(object);
187+
objects.push(object);
188188
}
189189
if (other.length > 0) {
190190
const len = other.length;
@@ -307,7 +307,7 @@ export async function getTTS(text: string[], language: string): Promise<TTSRespo
307307
console.error(`photo-audio-handler doesn't support '${language}' language`);
308308
throw new Error("Unable to send segment to TTS");
309309
}
310-
310+
311311
return fetch(serviceURL, {
312312
method: "POST",
313313
headers: {
@@ -375,18 +375,3 @@ export async function sendOSC(jsonFile: string, outFile: string, server: string,
375375
})
376376
]);
377377
}
378-
379-
export function renderingTitle(semseg: { "segments": Record<string, unknown>[] }, objDet: ObjDet, objGroup: ObjGroup): string {
380-
console.debug("Rendering title")
381-
const hasSemseg = (semseg !== undefined) && (semseg["segments"].length > 0);
382-
const hasObj = (objDet !== undefined) && (objGroup !== undefined) && (objDet["objects"].length > 0);
383-
if (hasSemseg && hasObj) {
384-
return "Regions, things, and people";
385-
}
386-
else if (hasSemseg) {
387-
return "Outlines of regions";
388-
}
389-
else {
390-
return "Things and people";
391-
}
392-
}

preprocessors/mmsemseg/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,6 @@ ENV FLASK_APP=segment.py
5353
USER python
5454

5555
HEALTHCHECK --interval=60s --timeout=10s --start-period=120s --retries=5 CMD curl -f http://localhost:5000/health || exit 1
56+
HEALTHCHECK --interval=3600s --timeout=30s --start-period=120s --retries=3 CMD curl -f http://localhost:5000/health/gpu || exit 1
5657

5758
CMD [ "gunicorn", "segment:app", "-b", "0.0.0.0:5000", "--capture-output", "--log-level=debug" ]

preprocessors/mmsemseg/segment.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import logging
3737
from config.logging_utils import configure_logging
3838
from datetime import datetime
39+
import subprocess
3940

4041
configure_logging()
4142
# configuration and checkpoint files
@@ -262,5 +263,62 @@ def health():
262263
}), 200
263264

264265

266+
@app.route("/health/gpu", methods=["GET"])
267+
def gpu_driver_health_check():
268+
"""
269+
Enhanced health check:
270+
- Verifies CUDA & NVIDIA drivers are working
271+
- Detects if the loaded NVIDIA driver matches `nvidia-smi`
272+
- Ensures the container is using the correct GPU runtime
273+
"""
274+
275+
# Check if CUDA is available
276+
if not torch.cuda.is_available():
277+
return jsonify({
278+
"status": "unhealthy",
279+
"message": "CUDA not available inside the container",
280+
"recommendation": "Check if the container is running with GPU \
281+
access (--gpus all)"
282+
}), 500
283+
284+
try:
285+
# Get installed NVIDIA driver version from nvidia-smi
286+
nvidia_smi_version = subprocess.check_output(
287+
["nvidia-smi", "--query-gpu=driver_version",
288+
"--format=csv,noheader"],
289+
text=True
290+
).strip()
291+
292+
# Get loaded driver version from /proc/driver/nvidia/version
293+
loaded_driver_version = subprocess.check_output(
294+
["cat", "/proc/driver/nvidia/version"], text=True
295+
).split("\n")[0]
296+
297+
# Ensure they match
298+
if nvidia_smi_version not in loaded_driver_version:
299+
return jsonify({
300+
"status": "unhealthy",
301+
"message": "NVIDIA driver mismatch detected",
302+
"nvidia_smi_version": nvidia_smi_version,
303+
"loaded_driver_version": loaded_driver_version,
304+
"recommendation": "Reboot the system to ensure the correct \
305+
driver is loaded?"
306+
}), 500
307+
308+
return jsonify({
309+
"status": "healthy",
310+
"message": "NVIDIA drivers and CUDA are working correctly",
311+
"nvidia_smi_version": nvidia_smi_version,
312+
"loaded_driver_version": loaded_driver_version
313+
}), 200
314+
315+
except Exception as e:
316+
return jsonify({
317+
"status": "unhealthy",
318+
"message": f"NVIDIA driver check failed: {str(e)}",
319+
"recommendation": "Check driver installation and restart system"
320+
}), 500
321+
322+
265323
if __name__ == "__main__":
266324
app.run(host='0.0.0.0', port=5000, debug=True)

0 commit comments

Comments
 (0)