2013-06-12 41 views
4

我必須使用MS DirectShow從攝像頭捕捉視頻幀(我只想要原始像素數據)。
我能夠構建Graph/Filter網絡(捕獲設備過濾器和ISampleGrabber)並實現回調(ISampleGrabberCB)。我收到適當大小的樣本。DirectShow ISampleGrabber:示例顛倒和顏色通道反轉

但是,它們總是顛倒(垂直翻轉,即不旋轉),而顏色通道是BGR順序(不是RGB)。

我試圖將BITMAPINFOHEADER中的biHeight字段設置爲正值和負值,但它沒有任何影響。根據MSDN文檔,ISampleGrapper :: SetMediaType()無論如何都會忽略視頻數據的格式塊。

這是我看到的(記錄用不同的攝像頭,不DS),什麼DirectShow的ISampleGrabber給我:「RGB」分別實際上是在紅,綠,藍:

Here is what I see (recorded with a different camera, not DS)

我使用的代碼Here is what DirectShow ISampleGrabber gives me.

樣品,稍作簡化:

// 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 capture)測試它時,它看起來很正常。

+2

我想當你從Sample Grabber取回數據時 - 你錯誤地對待了行的順序。它通常是從下到上並且按照相反的順序處理線條 - 因此是問題所在。 –

+0

Roman,感謝您的回覆,但是無法按照正常的行順序接收幀(從頂部開始)?無論如何,我不認爲相機以這種方式發送它們。 它也沒有解釋BRG顏色通道翻轉。 由於代碼也適用於其他相機,我希望能夠弄清楚發生了什麼... – Makx

+0

「正常」Windows RGB順序是自下而上的。一些組件能夠反轉它,但這是一個脆弱的假設。更強大的方法是讓它去原來的順序,或從底部到頂部。然後,如果需要,可以使用緩衝區來處理行的實際順序或自行反轉行。我猜攝像頭不會讓你失望,並且你的代碼片段並不能說服你在Sample Grabber緩衝區上從頭到尾。 –

回答

-1

我的快速破解了這一點:

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色彩空間是正確的,對於其他顏色空間的代碼需要加以糾正

0

我注意到,使用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); 
}