Skip to content

Commit 7ea4b5d

Browse files
committed
add opencv image support
1 parent 3d40d22 commit 7ea4b5d

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
@@ -548,6 +548,11 @@
548548
"command": "espIdf.viewAsLVGLImage",
549549
"when": "inDebugMode && debugType == 'gdbtarget' && debugState == stopped",
550550
"group": "navigation"
551+
},
552+
{
553+
"command": "espIdf.viewAsOpenCVImage",
554+
"when": "inDebugMode && debugType == 'gdbtarget' && debugState == stopped",
555+
"group": "navigation"
551556
}
552557
]
553558
},
@@ -1827,6 +1832,11 @@
18271832
"title": "%espIdf.viewAsLVGLImage.title%",
18281833
"category": "ESP-IDF"
18291834
},
1835+
{
1836+
"command": "espIdf.viewAsOpenCVImage",
1837+
"title": "%espIdf.viewAsOpenCVImage.title%",
1838+
"category": "ESP-IDF"
1839+
},
18301840
{
18311841
"command": "espIdf.openImageViewer",
18321842
"title": "%espIdf.openImageViewer.title%",

package.nls.es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"espIdf.welcome.title": "Bienvenido",
100100
"espIdf.viewAsHex.title": "Ver como Hexadecimal",
101101
"espIdf.viewAsLVGLImage.title": "Ver como Imagen LVGL",
102+
"espIdf.viewAsOpenCVImage.title": "Ver como Imagen OpenCV",
102103
"espIdf.openImageViewer.title": "Abrir Visor de Imágenes",
103104
"espIdf.hexView.copyValue.title": "Copiar valor al portapapeles",
104105
"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
@@ -97,6 +97,7 @@
9797
"espIdf.unitTest.installPyTest.title": "Unit Test: Install ESP-IDF PyTest Requirements",
9898
"espIdf.viewAsHex.title": "View as Hex",
9999
"espIdf.viewAsLVGLImage.title": "View as LVGL Image",
100+
"espIdf.viewAsOpenCVImage.title": "View as OpenCV Image",
100101
"espIdf.openImageViewer.title": "Open Image Viewer",
101102
"espIdf.hexView.copyValue.title": "Copy value to clipboard",
102103
"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
@@ -99,6 +99,7 @@
9999
"espIdf.welcome.title": "Bem-vindo",
100100
"espIdf.viewAsHex.title": "Ver como Hexadecimal",
101101
"espIdf.viewAsLVGLImage.title": "Ver como Imagem LVGL",
102+
"espIdf.viewAsOpenCVImage.title": "Ver como Imagem OpenCV",
102103
"espIdf.openImageViewer.title": "Abrir Visor de Imagens",
103104
"espIdf.hexView.copyValue.title": "Copiar valor para a área de transferência",
104105
"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
@@ -97,6 +97,7 @@
9797
"espIdf.unitTest.installPyTest.title": "Unit Test: Установка требований ESP-IDF PyTest.",
9898
"espIdf.viewAsHex.title": "Просмотреть как шестнадцатеричное",
9999
"espIdf.viewAsLVGLImage.title": "Просмотреть как изображение LVGL",
100+
"espIdf.viewAsOpenCVImage.title": "Просмотреть как изображение OpenCV",
100101
"espIdf.openImageViewer.title": "Открыть просмотрщик изображений",
101102
"espIdf.hexView.copyValue.title": "Скопировать значение в буфер обмена",
102103
"espIdf.hexView.deleteElement.title": "Удалить шестнадцатеричное значение из списка",

package.nls.zh-CN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
"espIdf.unitTest.installPyTest.title": "单元测试:安装 ESP-IDF PyTest 依赖项",
9898
"espIdf.viewAsHex.title": "以十六进制查看",
9999
"espIdf.viewAsLVGLImage.title": "以 LVGL 图像查看",
100+
"espIdf.viewAsOpenCVImage.title": "以 OpenCV 图像查看",
100101
"espIdf.openImageViewer.title": "打开图像查看器",
101102
"espIdf.hexView.copyValue.title": "复制值到剪贴板",
102103
"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
@@ -1573,6 +1573,50 @@ export async function activate(context: vscode.ExtensionContext) {
15731573
}
15741574
);
15751575

1576+
registerIDFCommand(
1577+
"espIdf.viewAsOpenCVImage",
1578+
(debugContext: {
1579+
container: {
1580+
expensive: boolean;
1581+
name: string;
1582+
variablesReference: number;
1583+
};
1584+
sessionId: string;
1585+
variable: {
1586+
evaluateName: string;
1587+
memoryReference: string;
1588+
name: string;
1589+
value: string;
1590+
variablesReference: number;
1591+
type: string;
1592+
};
1593+
}) => {
1594+
return PreCheck.perform([openFolderCheck], async () => {
1595+
if (
1596+
!debugContext ||
1597+
!debugContext.variable ||
1598+
!debugContext.variable.evaluateName
1599+
) {
1600+
return;
1601+
}
1602+
if (!vscode.debug.activeDebugSession) {
1603+
return;
1604+
}
1605+
1606+
try {
1607+
// Show the ImageViewPanel and pass the variable information
1608+
ImageViewPanel.show(context.extensionPath);
1609+
1610+
// Send the variable information to the ImageViewPanel
1611+
ImageViewPanel.handleOpenCVVariableFromContext(debugContext);
1612+
} catch (e) {
1613+
const msg = e && e.message ? e.message : e;
1614+
Logger.errorNotify(msg, e, "extension espIdf.viewAsOpenCVImage");
1615+
}
1616+
});
1617+
}
1618+
);
1619+
15761620
registerIDFCommand("espIdf.openImageViewer", () => {
15771621
return PreCheck.perform([openFolderCheck], () => {
15781622
// Show the ImageViewPanel without an image

0 commit comments

Comments
 (0)