Итак, я пытаюсь преобразовать ID3D11Texture2D в OpenCV cv::Mat, но мой код по какой-то причине искажает изображение.
D3D11_TEXTURE2D_DESC capturedTextureDesc;
texture->GetDesc(&capturedTextureDesc);
capturedTextureDesc.Usage = D3D11_USAGE_STAGING;
capturedTextureDesc.BindFlags = 0;
capturedTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
capturedTextureDesc.MiscFlags = 0;
winrt::com_ptr<ID3D11Texture2D> userTexture = nullptr;
winrt::check_hresult(d3dDevice->CreateTexture2D(&capturedTextureDesc, NULL, userTexture.put()));
d3dContext->CopyResource(userTexture.get(), texture.get());
D3D11_MAPPED_SUBRESOURCE resource;
winrt::check_hresult(d3dContext->Map(userTexture.get(), NULL, D3D11_MAP_READ, 0, &resource));
//https://arxiv.org/pdf/1702.02514
int scWidth = capturedTextureDesc.Width;
int scHeight = capturedTextureDesc.Height;
int CAMERA_CHANNELS = 4;
unsigned char* buffer = new unsigned char[(scWidth * scHeight * CAMERA_CHANNELS)];
unsigned char* mappedData = reinterpret_cast<unsigned char*>(resource.pData);
memcpy(buffer, mappedData, (scWidth * scHeight * CAMERA_CHANNELS));
d3dContext->Unmap(userTexture.get(), 0);
IplImage* frame = cvCreateImageHeader(cvSize(scWidth, scHeight),
IPL_DEPTH_8U, CAMERA_CHANNELS);
frame->imageData = (char*)buffer;
cvSetData(frame, buffer, frame->widthStep);
// cv::Mat mt = cv::Mat(frame, true); // constructor dissapered
//https://stackoverflow.com/questions/15925084/conversion-from-iplimage-to-cvmat
cv::Mat mt = cv::cvarrToMat(frame);
return mt;
Вот окно, полученное с помощью вывода cv::Mat:
Они имеют одинаковый размер, но содержимое разное.
Как получить шаг из исходной текстуры?
Добавил ответ с этой информацией. Также узнайте, почему нет необходимости использовать IplImage, чтобы наконец получить cv::Mat.





Код, похоже, не ссылается на значение RowPitch в D3D11_MAPPED_SUBRESOURCE. RowPitch не всегда width*pixelSize — каждая строка может быть буферизована таким образом, чтобы ширина строки была кратна 16 или другому значению.
Вместо этого:
memcpy(buffer, mappedData, (scWidth * scHeight * CAMERA_CHANNELS));
Копируем построчно:
for (int h = 0; h < scHeight; h++) {
unsigned char* srcRow = mappedData + (resource.RowPitch * h);
unsigned char* dstRow = buffer + scWidth * CAMERA_CHANNELS * h;
memcpy(dstRow, srcRow, scWidth*CAMERA_CHANNELS);
}
Или... передать rowPitch в OpenCV.
buffer = new unsigned char[resource.RowPitch * scHeight * CAMERA_CHANNELS];
memcpy(buffer, mappedData, resource.RowPitch * scHeight * CAMERA_CHANNELS);
...
cvSetData(frame, buffer, resource.RowPitch);
Вызовите MFCopyImage вместо цикла копирования, это будет быстрее.
@Soonts - я могу перепроверить, но я вполне уверен, что memcpy в последней версии Visual C++ имеет те же оптимизации SSE/SSE2, что и MFCopyImage. В некоторых контролируемых тестовых приложениях, скомпилированных с включенной оптимизацией, я думаю, что вижу инструкции SSE/SSE2 при прохождении сборки.
Проблема связана с неправильным шагом (он же шаг/шаг).
Шаг — это количество байтов в строке изображения.
Это действительно может быть scWidth * CAMERA_CHANNELS, но это не обязательно.
Иногда (из-за проблем с выравниванием) каждая строка дополняется несколькими байтами.
В вашем коде вы предполагаете, что такого заполнения нет, но, судя по изображению, которое вы разместили, оно действительно есть.
Решение состоит в том, чтобы использовать шаг из текстуры DirectX, то есть resource.rowPitch (вместо того, чтобы предполагать, что это scWidth * CAMERA_CHANNELS).
Другая проблема в том, что вам не обязательно использовать IplImage на промежуточном этапе. Поскольку вам нужен cv::Mat, вы можете использовать его напрямую. Вам даже не понадобится buffer — вы можете инициализировать cv::Mat непосредственно из сопоставленных данных:
unsigned char* mappedData = reinterpret_cast<unsigned char*>(resource.pData);
cv::Mat mt = cv::Mat(scHeight, scWidth, CV_8UC4, mappedData, resource.rowPitch).clone();
d3dContext->Unmap(userTexture.get(), 0);
return mt;
Примечание. clone() используется, потому что конструктор cv::Mat, который принимает данные и шаг, создает cv::Mat, которому данные не принадлежат. clone() создает копию, которой она принадлежит.
cvSetData(frame, buffer, frame->widthStep)следует использовать шаг (также известный как шаг/шаг) исходной текстуры DX.