将图像传递给 openCV 中的 blur() 或 Canny() 时出现类似幽灵的伪像

Ghost-like artifact when passing image to blur() or Canny() in openCV

所以我有一些 C++ opencv 代码,我在 unity 中从 C# 调用这些代码来处理来自网络摄像头的输入。

我将 C++ 代码编译为 DLL,然后使用 DLLImport 将其导入 C#。我将固定的 GCHandle 引用传递给 C++ 代码,以便我可以从 C++ 操作图像数组。这一切都有效。我可以将每一帧传递到我的 C++ dll 并对其进行灰度化。效果非常好。

当我尝试做除了使框架灰度化以外的事情时,问题就出现了。我试着做一个简单的 blur() ,输出结果是左右都有一个奇怪的重影图像。我不确定会出什么问题。当我执行 GaussianBlur() 或 Canny() 时也会发生这种情况。

左边是我遮住摄像头的时候,你可以更清楚的看到那个诡异的神器。中间是通过 GaussianBlur() 后的工件本身。它似乎创建了图像的副本并将它们与自身叠加。右边是灰度显示它正常工作的时候。所以我认为这不是 C# 和 C++ 之间发生的事情,只有当我通过 opencv 的模糊或 gaussianblur 或 canny 传递帧时才会发生。

这里是unity中的C#代码

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;

public class camera : MonoBehaviour {
    [DllImport("tee")]
    public static extern void bw([MarshalAs(UnmanagedType.LPStruct)]
        IntPtr data, 
        int width,
        int height);
    WebCamTexture back;
    Color32[] data;
    Byte[] byteData;
    Renderer rend;
    String test;
    Texture2D tex;
    GCHandle dataHandle;

    // Use this for initialization
    void Start () {
        back = new WebCamTexture();
        back.Play();
        rend = GetComponent<Renderer>();

        tex = new Texture2D(back.width, back.height, TextureFormat.ARGB32, false);
        data = back.GetPixels32();
        dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
    }
    void OnDisable()
    {
        dataHandle.Free();
    }

    // Update is called once per frame
    void Update () {
        back.GetPixels32(data);
        bw(dataHandle.AddrOfPinnedObject(), back.width, back.height);

        tex.SetPixels32(data);
        tex.Apply();
        rend.material.mainTexture = tex;
    }
}

这里是编译成 DLL 的 C++ 代码

#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;

extern "C"
{
    __declspec(dllexport) void bw(int data, int width, int height) {
        unsigned char * buffer = reinterpret_cast<unsigned char *>(data);
        Mat mat = Mat(width, height, CV_8UC4, buffer).clone();
        Mat gray;
        cvtColor(mat, gray, CV_RGBA2GRAY);

        Mat blurred;
        GaussianBlur(gray, blurred, Size(3, 3), 2, 2);

        if (blurred.isContinuous()) {
            for (int i = 0; i < (width * height); i++) {
                unsigned char * pxl = buffer + 4 * i;
                pxl[0] = blurred.data[i]; //red channel
                pxl[1] = blurred.data[i]; //green channel
                pxl[2] = blurred.data[i]; //blue channel
                pxl[3] = (unsigned char)255; // alpha channel
            }
        }
    }
}

我认为您的问题是您访问 blurred 像素值的方式。您应该使用以下方式访问通道值

for (int i = 0; i < (width * height); i++) {
                unsigned char * pxl = buffer + 4 * i;
                pxl[0] = blurred.ptr<uchar>(i); //red channel
                pxl[1] = blurred.ptr<uchar>(i); //green channel
                pxl[2] = blurred.ptr<uchar>(i); //blue channel
                pxl[3] = (unsigned char)255; // alpha channel
            }

您可以研究的另一件事是 opencv 存储像素值的方式与数据缓冲区的指针访问方式(。您可以通过在访问模糊图像之前旋转模糊图像来轻松测试它,看看这是否给您正确的而是输出或创建 Mat mat = Mat(height,width, CV_8UC4, buffer).clone();

你说的模糊类型是对的,灰度图应该是一个通道。

尝试使用当前代码以另一种方式访问​​模糊图像中的值

根据OpenCV的文档,Mat构造函数将rows和cols作为参数,所以你应该切换width和height参数。看这里http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-mat

另外一个问题,你知道C#中图片是怎么存储的吗?他们是否有任何类型的数据对齐(即行不连续)?因为当您创建包含 Mat 时,这也可能是一个问题。

我目前来自phone,我会尽快重新格式化我的答案

编辑:考虑一下,只有当 OpenCV 和 texture2D 都按行主要顺序存储图像时,在构建时切换宽度和高度的事情才有意义。我查了一下这里(http://rbwhitaker.wikidot.com/extracting-texture-data)好像是这样的