​Roader​ 2024-10-30 18:04 采纳率: 38.5%
浏览 10
已结题

opencv.js进行图像边缘化检测与描边内存溢出

opencv.js进行图像边缘化检测与描边内存溢出
以下为我的代码,主要使用了递归调用,也在调用完成后删除掉了初始化的mat对象之类的。但是依然内存溢出。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Video Object Detection</title>
</head>
<body>
<video id="video" width="1280" height="720" autoplay></video>
<canvas id="canvas" width="1280" height="720"></canvas>
<script async src="../archives/js/opencv.js" onload="onOpenCvReady();"></script>
<script>
    let video = document.getElementById('video');
    let canvasElement = document.getElementById('canvas');
    let ctx = canvasElement.getContext('2d');

    let gray;
    let denoised;
    let edges;
    let fst;
    let hierarchy;
    let approx;
    let dst;
    let dst1;
    let kernel;
    let fsk;
    let sss;

    function onOpenCvReady() {
        navigator.mediaDevices.getUserMedia({video: {width: 1280, height: 720}})
            .then(stream => {
                video.srcObject = stream;
                video.play();  // 开始播放视频
            })
            .catch(err => {
                console.error("Error accessing media devices.", err);
            });

        // 监听 'play' 事件,确保视频开始播放后再进行检测和绘制
        video.addEventListener('play', detectAndDraw);
    }

    function detectAndDraw() {
       // console.log("开始");
        init();
        // 确保视频正在播放
        if (video.paused || video.ended) return;

        // 绘制视频帧到 canvas 上
        ctx.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);

        // 获取 canvas 上的图像数据并转换为 OpenCV 的 Mat 对象
        let src = cv.matFromImageData(ctx.getImageData(0, 0, canvasElement.width, canvasElement.height));

        // 图像处理:转换为灰度图
        cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY); // 重用 gray 对象

        // 降噪处理
        cv.bilateralFilter(gray, denoised, 9, 75, 75); // 重用 denoised 对象

        // 高斯滤波
        cv.GaussianBlur(denoised, denoised, new cv.Size(5, 5), 0, 0); // 使用降噪后的图像

        // 提取边缘
        cv.Canny(denoised, edges, 75, 150); // 重用 edges 对象

        // 膨胀
        cv.dilate(edges, edges, kernel); // 重用 kernel 和 edges 对象

        // 进行腐蚀操作
        cv.erode(edges, fst, fsk); // 重用 fst 和 fsk 对象

        // 提取外轮廓
        let contours = new cv.MatVector();
        cv.findContours(edges, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE);

        let index = 0, maxArea = 0, aindex = 0;
        const area = src.cols * src.rows;

        for (let i = 0; i < contours.size(); ++i) {
            let tempArea = Math.abs(cv.contourArea(contours.get(i)));
            if (tempArea > maxArea) {
                index = i;
                maxArea = tempArea;
            }
            if (tempArea > area * 0.03) {
                //console.log("底图面积:" + area + ";文件面积:" + tempArea);
                aindex++;
            }
        }

        if (maxArea > 0) {
            const foundContour = contours.get(index);
            const arcL = cv.arcLength(foundContour, true);
            cv.approxPolyDP(foundContour, approx, 0.02 * arcL, true); // 重用 approx 对象

            if (approx.total() === 4) {
                let points = [];
                const data32S = approx.data32S;
                for (let i = 0, len = data32S.length / 2; i < len; i++) {
                    points[i] = {x: data32S[i * 2], y: data32S[i * 2 + 1]};
                }
                //console.log("检测到四边形点:", points);

                let srcPoints = getSortedVertex(points).flatMap(p => [p.x, p.y]);

                // 在源图像上绘制绿色边框
                for (let i = 0; i < 4; i++) {
                    const startX = srcPoints[i * 2];
                    const startY = srcPoints[i * 2 + 1];
                    const endX = srcPoints[((i + 1) % 4) * 2];
                    const endY = srcPoints[((i + 1) % 4) * 2 + 1];

                    // 绘制线段,颜色为绿色,线宽为2
                    cv.line(src, new cv.Point(startX, startY), new cv.Point(endX, endY), new cv.Scalar(0, 255, 0, 255), 10);
                }
                cv.imshow(canvasElement, src);


                gray.delete();
                denoised.delete();
                edges.delete();
                fst.delete();
                hierarchy.delete();
                approx.delete();
                dst.delete();
                dst1.delete();
                kernel.delete();
                fsk.delete();
                contours.delete();

            }
        }

        requestAnimationFrame(detectAndDraw);
    }


    // 开始处理
    video.addEventListener('play', () => {
        detectAndDraw();
    });

    function getSortedVertex(points) {
        // 计算中心点
        const center = {
            x: points.reduce((sum, p) => sum + p.x, 0) / points.length,
            y: points.reduce((sum, p) => sum + p.y, 0) / points.length
        };

        // 计算每个点到中心点的角度
        const angles = points.map(p => {
            return {
                point: p,
                angle: Math.atan2(p.y - center.y, p.x - center.x) * 180 / Math.PI
            };
        });

        // 按角度排序
        const sortedPoints = angles.sort((a, b) => a.angle - b.angle).map(a => a.point);
        return sortedPoints;
    }


    function init() {
        //console.log("初始化");
        sss = 1;
        gray = new cv.Mat();
        denoised = new cv.Mat();
        edges = new cv.Mat();
        fst = new cv.Mat();
        hierarchy = new cv.Mat();
        approx = new cv.Mat();
        dst = new cv.Mat();
        dst1 = new cv.Mat();
        kernel = cv.getStructuringElement(cv.MORPH_RECT, new cv.Size(7, 7));
        fsk = cv.getStructuringElement(cv.MORPH_RECT, new cv.Size(5, 5));
    }
</script>
</body>
</html>
  • 写回答

1条回答 默认 最新

  • 无疆76 2024-10-30 18:05
    关注

    以下回复参考:皆我百晓生券券喵儿等免费微信小程序作答:

    从你的代码来看,内存溢出可能有几个原因。以下是一些可能的解决方案:

    1. 确保释放所有资源:你已经开始释放一些资源,但是可能有未删除或未及时删除的变量和对象。在你完成了图像处理操作之后,应该删除所有的cv.Mat对象以确保他们不会被保留在内存中。请注意,如果你对同一对象进行了多次赋值或引用,可能需要对所有这些引用都进行删除操作。这可能需要仔细检查你的代码并找出所有的变量分配和引用。
    2. 避免重复初始化对象:在每次递归调用中初始化新的cv.Mat对象可能会导致内存问题。如果可能的话,尝试将这些对象的初始化移出循环和递归调用。比如将init()函数的调用移到代码的开始部分而不是每次递归调用。同时,你也可以尝试重用某些对象,如cv.MatVector对象和轮廓信息(而不是每次都重新创建它们)。这样做可以提高效率并避免内存泄漏。如果cv库有相应的API提供的话,可以考虑使用cv库的垃圾回收机制。例如,你可以使用cv.clear来清除对象。
    3. 优化图像处理流程:在某些情况下,图像处理流程可能过于复杂或需要大量内存。尝试简化你的图像处理流程,或者减少图像处理的分辨率和复杂度。例如,如果你在处理高分辨率的图像时遇到内存问题,可以尝试降低图像的分辨率。此外,如果可能的话,尝试使用更高效的算法或数据结构来存储和处理图像数据。
    4. 使用异步处理:JavaScript是异步的,你可以尝试使用异步处理来避免阻塞主线程并可能地降低内存消耗。这可以通过在Web Worker中使用你的图像处理代码来实现,这样可以在后台线程中运行你的代码而不会阻塞主线程。然而,请注意OpenCV.js可能不支持Web Worker或其他异步方法处理。如果你发现这种方法的限制过多或难以使用,请寻找其他图像处理库或使用浏览器原生的图像处理API。此外,使用异步处理还需要考虑如何管理异步操作的内存管理问题。你需要确保及时清理不再需要的资源并避免创建过多的同时运行的任务以防止内存溢出。你可以使用Promise或async/await来处理异步操作并确保正确地管理资源。另外注意不要在回调函数中创建新的对象或变量以避免作用域问题导致的内存泄漏问题。在JavaScript中,全局变量和闭包可能会导致内存泄漏问题因为它们不会随着函数调用结束而被销毁或回收(如果无法访问)。确保避免创建不必要的全局变量或复杂的闭包结构以防止内存泄漏问题。另外注意不要在回调函数中创建新的对象或变量以避免作用域问题导致的内存泄漏问题对于处理大型数据集可能需要特别注意优化和管理资源避免过度使用内存这可能包括通过分批处理数据或者优化数据结构等方式来实现(由于我不知道你具体的处理方式难以给出具体示例)。使用Profiler工具检测并分析程序的性能帮助识别潜在的问题并找到解决方案。例如Chrome开发者工具中的Memory Profiler可以帮助你分析内存使用情况并找到可能的内存泄漏点。你也可以尝试使用其他工具如VisualVM等来分析你的JavaScript代码的内存使用情况。总之你需要确保你的代码正确地管理了内存并及时释放不再需要的资源以避免内存溢出问题同时优化你的代码和算法以提高性能和效率。希望这些建议能帮到你!如果你能提供更多关于内存溢出的详细信息例如错误消息或使用Profiler工具的结果等我可能能够提供更具体的解决方案或者可以查阅OpenCV.js的文档和社区以获取更多关于内存管理和优化的建议和信息
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 11月9日
  • 已采纳回答 11月1日
  • 创建了问题 10月30日