Skip to content

Commit f1f3569

Browse files
committed
add opencv image support
1 parent 0f4c17e commit f1f3569

File tree

9 files changed

+248
-90
lines changed

9 files changed

+248
-90
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,11 @@
533533
"command": "espIdf.viewAsLVGLImage",
534534
"when": "inDebugMode && debugType == 'gdbtarget' && debugState == stopped",
535535
"group": "navigation"
536+
},
537+
{
538+
"command": "espIdf.viewAsOpenCVImage",
539+
"when": "inDebugMode && debugType == 'gdbtarget' && debugState == stopped",
540+
"group": "navigation"
536541
}
537542
]
538543
},
@@ -1780,6 +1785,11 @@
17801785
"title": "%espIdf.viewAsLVGLImage.title%",
17811786
"category": "ESP-IDF"
17821787
},
1788+
{
1789+
"command": "espIdf.viewAsOpenCVImage",
1790+
"title": "%espIdf.viewAsOpenCVImage.title%",
1791+
"category": "ESP-IDF"
1792+
},
17831793
{
17841794
"command": "espIdf.openImageViewer",
17851795
"title": "%espIdf.openImageViewer.title%",

package.nls.es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"espIdf.welcome.title": "Bienvenido",
9999
"espIdf.viewAsHex.title": "Ver como Hexadecimal",
100100
"espIdf.viewAsLVGLImage.title": "Ver como Imagen LVGL",
101+
"espIdf.viewAsOpenCVImage.title": "Ver como Imagen OpenCV",
101102
"espIdf.openImageViewer.title": "Abrir Visor de Imágenes",
102103
"espIdf.hexView.copyValue.title": "Copiar valor al portapapeles",
103104
"espIdf.hexView.deleteElement.title": "Eliminar valor hexadecimal de la lista",

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"espIdf.unitTest.installPyTest.title": "Unit Test: Install ESP-IDF PyTest Requirements",
9797
"espIdf.viewAsHex.title": "View as Hex",
9898
"espIdf.viewAsLVGLImage.title": "View as LVGL Image",
99+
"espIdf.viewAsOpenCVImage.title": "View as OpenCV Image",
99100
"espIdf.openImageViewer.title": "Open Image Viewer",
100101
"espIdf.hexView.copyValue.title": "Copy value to clipboard",
101102
"espIdf.hexView.deleteElement.title": "Delete hex value from list",

package.nls.pt.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"espIdf.welcome.title": "Bem-vindo",
9999
"espIdf.viewAsHex.title": "Ver como Hexadecimal",
100100
"espIdf.viewAsLVGLImage.title": "Ver como Imagem LVGL",
101+
"espIdf.viewAsOpenCVImage.title": "Ver como Imagem OpenCV",
101102
"espIdf.openImageViewer.title": "Abrir Visor de Imagens",
102103
"espIdf.hexView.copyValue.title": "Copiar valor para a área de transferência",
103104
"espIdf.hexView.deleteElement.title": "Excluir valor hexadecimal da lista",

package.nls.ru.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"espIdf.unitTest.installPyTest.title": "Unit Test: Установка требований ESP-IDF PyTest.",
9797
"espIdf.viewAsHex.title": "Просмотреть как шестнадцатеричное",
9898
"espIdf.viewAsLVGLImage.title": "Просмотреть как изображение LVGL",
99+
"espIdf.viewAsOpenCVImage.title": "Просмотреть как изображение OpenCV",
99100
"espIdf.openImageViewer.title": "Открыть просмотрщик изображений",
100101
"espIdf.hexView.copyValue.title": "Скопировать значение в буфер обмена",
101102
"espIdf.hexView.deleteElement.title": "Удалить шестнадцатеричное значение из списка",

package.nls.zh-CN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"espIdf.unitTest.installPyTest.title": "单元测试:安装 ESP-IDF PyTest 依赖项",
9797
"espIdf.viewAsHex.title": "以十六进制查看",
9898
"espIdf.viewAsLVGLImage.title": "以 LVGL 图像查看",
99+
"espIdf.viewAsOpenCVImage.title": "以 OpenCV 图像查看",
99100
"espIdf.openImageViewer.title": "打开图像查看器",
100101
"espIdf.hexView.copyValue.title": "复制值到剪贴板",
101102
"espIdf.hexView.deleteElement.title": "从列表中删除十六进制值",

src/cdtDebugAdapter/imageViewPanel.ts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,29 @@ export class ImageViewPanel {
8989
}
9090
}
9191

92+
public static handleOpenCVVariableFromContext(debugContext: {
93+
container: {
94+
expensive: boolean;
95+
name: string;
96+
variablesReference: number;
97+
};
98+
sessionId: string;
99+
variable: {
100+
evaluateName: string;
101+
memoryReference: string;
102+
name: string;
103+
value: string;
104+
variablesReference: number;
105+
type: string;
106+
};
107+
}) {
108+
if (ImageViewPanel.instance) {
109+
ImageViewPanel.instance.handleExtractOpenCVImageProperties(
110+
debugContext.variable
111+
);
112+
}
113+
}
114+
92115
private constructor(panel: vscode.WebviewPanel, extensionPath: string) {
93116
this.panel = panel;
94117
this.extensionPath = extensionPath;
@@ -506,6 +529,142 @@ export class ImageViewPanel {
506529
}
507530
}
508531

532+
private async handleExtractOpenCVImageProperties(variableName: {
533+
evaluateName: string;
534+
memoryReference: string;
535+
name: string;
536+
value: string;
537+
variablesReference: number;
538+
type: string;
539+
}) {
540+
try {
541+
const session = vscode.debug.activeDebugSession;
542+
if (!session) {
543+
this.panel.webview.postMessage({
544+
command: "showError",
545+
error: "No active debug session found",
546+
});
547+
return;
548+
}
549+
550+
// Check if the variable is of type cv::Mat
551+
if (variableName.type.indexOf("cv::Mat") === -1 && variableName.type.indexOf("Mat") === -1) {
552+
this.panel.webview.postMessage({
553+
command: "showError",
554+
error: `Variable ${variableName.name} is not of type cv::Mat`,
555+
});
556+
return;
557+
}
558+
559+
// Get the Mat object's children
560+
const matChildren = await session.customRequest("variables", {
561+
variablesReference: variableName.variablesReference,
562+
});
563+
564+
if (!matChildren || !matChildren.variables) {
565+
this.panel.webview.postMessage({
566+
command: "showError",
567+
error: `No children found for variable ${variableName.name}`,
568+
});
569+
return;
570+
}
571+
572+
// Extract OpenCV Mat properties
573+
const imageProperties: ImageWithDimensionsElement = {
574+
name: variableName.name,
575+
data: new Uint8Array(),
576+
width: 0,
577+
height: 0,
578+
format: 0,
579+
};
580+
581+
// Extract rows, cols, and data from the Mat structure
582+
matChildren.variables.forEach((child: any) => {
583+
if (child.name === "rows") {
584+
imageProperties.height = parseInt(child.value, 10);
585+
} else if (child.name === "cols") {
586+
imageProperties.width = parseInt(child.value, 10);
587+
} else if (child.name === "data") {
588+
const match = child.value.match(/0x[0-9a-fA-F]+/);
589+
if (match) {
590+
imageProperties.dataAddress = match[0];
591+
}
592+
} else if (child.name === "step") {
593+
// step[0] contains bytes per row
594+
// We'll need to get the step array children
595+
}
596+
});
597+
598+
// Get step array to determine bytes per row
599+
const stepObj = matChildren.variables.find(
600+
(child: any) => child.name === "step"
601+
);
602+
603+
let bytesPerRow = 0;
604+
if (stepObj) {
605+
const stepChildren = await session.customRequest("variables", {
606+
variablesReference: stepObj.variablesReference,
607+
});
608+
609+
if (stepChildren && stepChildren.variables) {
610+
// step[0] is bytes per row
611+
const step0 = stepChildren.variables.find((child: any) => child.name === "[0]");
612+
if (step0) {
613+
bytesPerRow = parseInt(step0.value, 10);
614+
}
615+
}
616+
}
617+
618+
// Calculate data size: rows * bytes_per_row
619+
if (imageProperties.height > 0 && bytesPerRow > 0) {
620+
imageProperties.dataSize = imageProperties.height * bytesPerRow;
621+
} else {
622+
// Fallback: estimate based on common OpenCV formats
623+
// Assume 3 channels (BGR) if we can't determine
624+
imageProperties.dataSize = imageProperties.width * imageProperties.height * 3;
625+
}
626+
627+
// Validate that we have the required data
628+
if (!imageProperties.dataAddress || !imageProperties.dataSize ||
629+
imageProperties.width <= 0 || imageProperties.height <= 0) {
630+
this.panel.webview.postMessage({
631+
command: "showError",
632+
error: `Could not extract complete OpenCV Mat properties from variable ${variableName.name}. Width: ${imageProperties.width}, Height: ${imageProperties.height}, DataSize: ${imageProperties.dataSize}`,
633+
});
634+
return;
635+
}
636+
637+
// Read memory data
638+
const readResponse = await session.customRequest("readMemory", {
639+
memoryReference: imageProperties.dataAddress,
640+
count: imageProperties.dataSize,
641+
});
642+
643+
if (readResponse && readResponse.data) {
644+
const binaryData = Buffer.from(readResponse.data, "base64");
645+
imageProperties.data = new Uint8Array(binaryData);
646+
647+
// OpenCV Mat typically uses BGR888 format (3 bytes per pixel)
648+
// Map this to our bgr888 format
649+
imageProperties.format = 0x0E; // Map to BGR888 equivalent
650+
651+
// Update the panel title and send the data
652+
this.panel.title = `Image Viewer: ${variableName.name} (OpenCV Mat)`;
653+
this.sendImageWithDimensionsData(imageProperties);
654+
} else {
655+
this.panel.webview.postMessage({
656+
command: "showError",
657+
error: `Could not read memory data for variable ${variableName.name}`,
658+
});
659+
}
660+
} catch (error) {
661+
this.panel.webview.postMessage({
662+
command: "showError",
663+
error: `Error extracting OpenCV Mat image properties: ${error}`,
664+
});
665+
}
666+
}
667+
509668
private getHtmlContent(webview: vscode.Webview): string {
510669
const scriptPath = webview.asWebviewUri(
511670
vscode.Uri.file(

src/extension.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,50 @@ export async function activate(context: vscode.ExtensionContext) {
15261526
}
15271527
);
15281528

1529+
registerIDFCommand(
1530+
"espIdf.viewAsOpenCVImage",
1531+
(debugContext: {
1532+
container: {
1533+
expensive: boolean;
1534+
name: string;
1535+
variablesReference: number;
1536+
};
1537+
sessionId: string;
1538+
variable: {
1539+
evaluateName: string;
1540+
memoryReference: string;
1541+
name: string;
1542+
value: string;
1543+
variablesReference: number;
1544+
type: string;
1545+
};
1546+
}) => {
1547+
return PreCheck.perform([openFolderCheck], async () => {
1548+
if (
1549+
!debugContext ||
1550+
!debugContext.variable ||
1551+
!debugContext.variable.evaluateName
1552+
) {
1553+
return;
1554+
}
1555+
if (!vscode.debug.activeDebugSession) {
1556+
return;
1557+
}
1558+
1559+
try {
1560+
// Show the ImageViewPanel and pass the variable information
1561+
ImageViewPanel.show(context.extensionPath);
1562+
1563+
// Send the variable information to the ImageViewPanel
1564+
ImageViewPanel.handleOpenCVVariableFromContext(debugContext);
1565+
} catch (e) {
1566+
const msg = e && e.message ? e.message : e;
1567+
Logger.errorNotify(msg, e, "extension espIdf.viewAsOpenCVImage");
1568+
}
1569+
});
1570+
}
1571+
);
1572+
15291573
registerIDFCommand("espIdf.openImageViewer", () => {
15301574
return PreCheck.perform([openFolderCheck], () => {
15311575
// Show the ImageViewPanel without an image

0 commit comments

Comments
 (0)