【问题标题】:DirectShow ISampleGrabber: samples are upside-down and color channels reverseDirectShow ISampleGrabber:样本上下颠倒,颜色通道反转
【发布时间】:2013-06-08 06:15:47
【问题描述】:

我必须使用 MS DirectShow 从摄像头捕获视频帧(我只想要原始像素数据)。
我能够构建图形/过滤器网络(捕获设备过滤器和 ISampleGrabber)并实现回调(ISampleGrabberCB)。我收到大小合适的样品。

但是,它们总是上下颠倒(垂直翻转,即不旋转),颜色通道是 BGR 顺序(不是 RGB)。

我尝试将 BITMAPINFOHEADER 中的 biHeight 字段设置为正值和负值,但没有任何效果。根据 MSDN 文档,ISampleGrapper::SetMediaType() 无论如何都会忽略视频数据的格式块。

这是我看到的(用不同的相机记录,而不是 DS),以及 DirectShow ISampleGrabber 给我的:“RGB”实际上分别是红色、绿色和蓝色:

我正在使用的代码示例,略微简化:

// Setting the media type...
AM_MEDIA_TYPE*   media_type   = 0 ;
this->ds.device_streamconfig->GetFormat(&media_type); // The IAMStreamConfig of the capture device
// Find the BMI header in the media type struct
BITMAPINFOHEADER* bmi_header;
if (media_type->formattype != FORMAT_VideoInfo) {
    bmi_header = &((VIDEOINFOHEADER*)media_type->pbFormat)->bmiHeader;
} else if (media_type->formattype != FORMAT_VideoInfo2) {
    bmi_header = &((VIDEOINFOHEADER2*)media_type->pbFormat)->bmiHeader;
} else {
    return false;
}
// Apply changes
media_type->subtype  = MEDIASUBTYPE_RGB24;
bmi_header->biWidth  = width;
bmi_header->biHeight = height;
// Set format to video device
this->ds.device_streamconfig->SetFormat(media_type);
// Set format for sample grabber
// bmi_header->biHeight = -(height); // tried this for either and both interfaces, no effect
this->ds.sample_grabber->SetMediaType(media_type);

// Connect filter pins
IPin* out_pin= getFilterPin(this->ds.device_filter, OUT,  0); // IBaseFilter interface for the capture device
IPin* in_pin = getFilterPin(this->ds.sample_grabber_filter,  IN,  0); // IBaseFilter interface for the sample grabber filter
out_pin->Connect(in_pin, media_type);

// Start capturing by callback
this->ds.sample_grabber->SetBufferSamples(false);
this->ds.sample_grabber->SetOneShot(false);
this->ds.sample_grabber->SetCallback(this, 1);
// start recording
this->ds.media_control->Run(); // IMediaControl interface

我正在检查每个函数的返回类型,没有收到任何错误。

感谢任何提示或想法。

我已经尝试过的事情:

  1. 将捕获设备过滤器或采样采集器的 biHeight 字段设置为负值,或者两者都设置为负值,或者两者都不设置 - 没有任何效果。

  2. 使用 IGraphBuilder 连接引脚 - 同样的问题。

  3. 在更改媒体类型之前连接引脚 - 同样的问题。

  4. 通过再次查询来检查过滤器是否实际应用了媒体类型 - 但它显然已应用或至少已存储。

  5. 将图像解释为总字节反转(最后一个字节在前,第一个字节在后) - 然后水平翻转。

  6. 检查摄像机是否有问题 - 当我使用 VLC(DirectShow 捕获)对其进行测试时,它看起来正常。

【问题讨论】:

  • 我想当您从 Sample Grabber 获取数据时 - 您错误地处理了行的顺序。它通常是自下而上的,您以相反的顺序处理行 - 因此问题。
  • Roman,感谢您的回复,但是不能按正常的行顺序(从顶部开始)接收帧吗?无论如何,我认为相机不会以这种方式发送它们。它也没有解释 BRG 颜色通道翻转。由于代码稍后也应该适用于其他相机,我希望能够弄清楚发生了什么......
  • “正常”Windows RGB 顺序是从下到上。一些组件能够扭转它,但这是一个脆弱的假设。更强大的方法是让它去原始顺序,或者强制从下到上。然后让缓冲区已经可用于处理行的实际顺序或如果需要自己反转行。我想相机不会让你失望,而且你的代码 sn-p 并没有说服我你让它在 Sample Grabber 缓冲区上从上到下。
  • 罗曼,再次感谢您的回答。 “你的代码 sn-p 不能说服我你让它在 Sample Grabber 缓冲区上从上到下” - 这正是重点:我试图让它“从上到下”,但我无法让它工作.最佳解决方案是捕获设备立即捕获它以避免不必要的翻转。我尝试设置 biHeight 负数,根据 windows 应该有这种效果,但它不起作用。另外:我仍然坚持 BGR 颜色通道翻转,这显然只发生在我身上。你能想象这是什么原因吗?
  • 大多数捕获设备和转换过滤器都无法实现从上到下的 RGB。只有稀有的过滤器是。

标签: c++ windows directshow video-capture


【解决方案1】:

我注意到当使用 I420 色彩空间时,转向消失了。 此外,目前大多数编解码器 (VP8) 都用作格式原始 I/O I420 色彩空间。

我在颜色空间I420中写了一个简单的镜像帧函数。

void Camera::OutputCallback(unsigned char* data, int len, uint32_t timestamp, void *instance_)
{
    Camera *instance = reinterpret_cast<Camera*>(instance_);

    Transport::RTPPacket packet;

    packet.rtpHeader.ts = timestamp;

    packet.payload = data;
    packet.payloadSize = len;

    if (instance->mirror)
    {
        Video::ResolutionValues rv = Video::GetValues(instance->resolution);
        int k = 0;

        // Chroma values
        for (int i = 0; i != rv.height; ++i)
        {
            for (int j = rv.width; j != 0; --j)
            {
                int l = ((rv.width * i) + j);
                instance->buffer[k++] = data[l];
            }
        }

        // U values
        for (int i = 0; i != rv.height/2; ++i)
        {
            for (int j = (rv.width/2); j != 0; --j)
            {
                int l = (((rv.width / 2) * i) + j) + rv.height*rv.width;
                instance->buffer[k++] = data[l];
            }
        }

        // V values
        for (int i = 0; i != rv.height / 2; ++i)
        {
            for (int j = (rv.width / 2); j != 0; --j)
            {
                int l = (((rv.width / 2) * i) + j) + rv.height*rv.width + (rv.width/2)*(rv.height/2);
                if (l == len)
                {
                    instance->buffer[k++] = 0;
                }
                else
                {
                    instance->buffer[k++] = data[l];
                }
            }
        }

        packet.payload = instance->buffer;
    }

    instance->receiver->Send(packet);
}

【讨论】:

    【解决方案2】:

    我对此的快速破解:

    void Camera::OutputCallback(unsigned char* data, int len, void *instance_)
    {
        Camera *instance = reinterpret_cast<Camera*>(instance_);
    
        int j = 0;
        for (int i = len-4; i > 0; i-=4)
        {
            instance->buffer[j] = data[i];
            instance->buffer[j + 1] = data[i + 1];
            instance->buffer[j + 2] = data[i + 2];
            instance->buffer[j + 3] = data[i + 3];
            j += 4;
        }
    
        Transport::RTPPacket packet;
    
        packet.payload = instance->buffer;
        packet.payloadSize = len;
    
        instance->receiver->Send(packet);
    }
    

    在 RGB32 颜色空间上是正确的,对于其他颜色空间,此代码需要更正

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-02
      • 1970-01-01
      • 2018-10-31
      • 1970-01-01
      • 1970-01-01
      • 2017-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多