videoio: add support for obsensor (Orbbec RGB-D Camera ) (#22196)
* videoio: add support for obsensor (Orbbec RGB-D Camera ) * obsensor: code format issues fixed and some code optimized * obsensor: fix typo and format issues * obsensor: fix crosses initialization errorpull/22313/head
parent
ebaf8cc06c
commit
fc3e393516
17 changed files with 1992 additions and 1 deletions
@ -0,0 +1,18 @@ |
|||||||
|
# --- obsensor --- |
||||||
|
if(NOT HAVE_OBSENSOR) |
||||||
|
if(WIN32) |
||||||
|
check_include_file(mfapi.h HAVE_MFAPI) |
||||||
|
if(HAVE_MFAPI) |
||||||
|
set(HAVE_OBSENSOR TRUE) |
||||||
|
set(HAVE_OBSENSOR_MSMF TRUE) |
||||||
|
ocv_add_external_target(obsensor "" "" "HAVE_OBSENSOR;HAVE_OBSENSOR_MSMF") |
||||||
|
endif() |
||||||
|
elseif(UNIX) |
||||||
|
check_include_file(linux/videodev2.h HAVE_CAMV4L2) |
||||||
|
if(HAVE_CAMV4L2) |
||||||
|
set(HAVE_OBSENSOR TRUE) |
||||||
|
set(HAVE_OBSENSOR_V4L2 TRUE) |
||||||
|
ocv_add_external_target(obsensor "" "" "HAVE_OBSENSOR;HAVE_OBSENSOR_V4L2") |
||||||
|
endif() |
||||||
|
endif() |
||||||
|
endif() |
@ -0,0 +1,103 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_INTERFACE_HPP |
||||||
|
#define OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_INTERFACE_HPP |
||||||
|
|
||||||
|
#ifdef HAVE_OBSENSOR |
||||||
|
|
||||||
|
#include "../precomp.hpp" // #include "precomp.hpp : compile error on linux |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <vector> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
enum StreamType |
||||||
|
{ |
||||||
|
OBSENSOR_STREAM_IR = 1, |
||||||
|
OBSENSOR_STREAM_COLOR = 2, |
||||||
|
OBSENSOR_STREAM_DEPTH = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
enum FrameFormat |
||||||
|
{ |
||||||
|
FRAME_FORMAT_UNKNOWN = -1, |
||||||
|
FRAME_FORMAT_YUYV = 0, |
||||||
|
FRAME_FORMAT_MJPG = 5, |
||||||
|
FRAME_FORMAT_Y16 = 8, |
||||||
|
}; |
||||||
|
|
||||||
|
enum PropertyId |
||||||
|
{ |
||||||
|
DEPTH_TO_COLOR_ALIGN = 42, |
||||||
|
CAMERA_PARAM = 1001, |
||||||
|
}; |
||||||
|
|
||||||
|
struct Frame |
||||||
|
{ |
||||||
|
FrameFormat format; |
||||||
|
uint32_t width; |
||||||
|
uint32_t height; |
||||||
|
uint32_t dataSize; |
||||||
|
uint8_t* data; |
||||||
|
}; |
||||||
|
|
||||||
|
struct StreamProfile |
||||||
|
{ |
||||||
|
uint32_t width; |
||||||
|
uint32_t height; |
||||||
|
uint32_t fps; |
||||||
|
FrameFormat format; |
||||||
|
}; |
||||||
|
|
||||||
|
struct CameraParam |
||||||
|
{ |
||||||
|
float p0[4]; |
||||||
|
float p1[4]; |
||||||
|
float p2[9]; |
||||||
|
float p3[3]; |
||||||
|
float p4[5]; |
||||||
|
float p5[5]; |
||||||
|
uint32_t p6[2]; |
||||||
|
uint32_t p7[2]; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef std::function<void(Frame*)> FrameCallback; |
||||||
|
class IStreamChannel |
||||||
|
{ |
||||||
|
public: |
||||||
|
virtual ~IStreamChannel() noexcept {} |
||||||
|
virtual void start(const StreamProfile& profile, FrameCallback frameCallback) = 0; |
||||||
|
virtual void stop() = 0; |
||||||
|
virtual bool setProperty(int propId, const uint8_t* data, uint32_t dataSize) = 0; |
||||||
|
virtual bool getProperty(int propId, uint8_t* recvData, uint32_t* recvDataSize) = 0; |
||||||
|
|
||||||
|
virtual StreamType streamType() const = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
// "StreamChannelGroup" mean a group of stream channels from same one physical device
|
||||||
|
std::vector<Ptr<IStreamChannel>> getStreamChannelGroup(uint32_t groupIdx = 0); |
||||||
|
|
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR
|
||||||
|
#endif // OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_INTERFACE_HPP
|
@ -0,0 +1,505 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_OBSENSOR_MSMF |
||||||
|
|
||||||
|
#include "obsensor_stream_channel_msmf.hpp" |
||||||
|
|
||||||
|
#include <shlwapi.h> // QISearch |
||||||
|
|
||||||
|
#pragma warning(disable : 4503) |
||||||
|
#pragma comment(lib, "mfplat") |
||||||
|
#pragma comment(lib, "mf") |
||||||
|
#pragma comment(lib, "mfuuid") |
||||||
|
#pragma comment(lib, "Strmiids") |
||||||
|
#pragma comment(lib, "Mfreadwrite") |
||||||
|
#pragma comment(lib, "dxgi") |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
std::string wideCharToUTF8(const WCHAR* s) |
||||||
|
{ |
||||||
|
auto len = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, nullptr, nullptr); |
||||||
|
if (len == 0) |
||||||
|
return ""; |
||||||
|
std::string buffer(len - 1, ' '); |
||||||
|
len = WideCharToMultiByte(CP_UTF8, 0, s, -1, &buffer[0], static_cast<int>(buffer.size()) + 1, nullptr, nullptr); |
||||||
|
return buffer; |
||||||
|
} |
||||||
|
|
||||||
|
std::string hr_to_string(HRESULT hr) |
||||||
|
{ |
||||||
|
_com_error err(hr); |
||||||
|
std::stringstream ss; |
||||||
|
ss << "HResult 0x" << std::hex << hr << ": \"" << err.ErrorMessage() << "\""; |
||||||
|
return ss.str(); |
||||||
|
} |
||||||
|
|
||||||
|
#define HR_FAILED_RETURN(x) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_INFO(NULL, "Media Foundation error return: " << hr_to_string(x)); \
|
||||||
|
return; \
|
||||||
|
} |
||||||
|
|
||||||
|
#define HR_FAILED_LOG(x) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_INFO(NULL, "Media Foundation error return: " << hr_to_string(x)); \
|
||||||
|
} |
||||||
|
|
||||||
|
#define HR_FAILED_EXEC(x, statement) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_INFO(NULL, "Media Foundation error return: " << hr_to_string(x)); \
|
||||||
|
statement; \
|
||||||
|
} |
||||||
|
|
||||||
|
std::vector<std::string> stringSplit(std::string string, char separator) |
||||||
|
{ |
||||||
|
std::vector<std::string> tokens; |
||||||
|
std::string::size_type i1 = 0; |
||||||
|
while (true) |
||||||
|
{ |
||||||
|
auto i2 = string.find(separator, i1); |
||||||
|
if (i2 == std::string::npos) |
||||||
|
{ |
||||||
|
tokens.push_back(string.substr(i1)); |
||||||
|
return tokens; |
||||||
|
} |
||||||
|
tokens.push_back(string.substr(i1, i2 - i1)); |
||||||
|
i1 = i2 + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool parseUvcDeviceSymbolicLink(const std::string& symbolicLink, uint16_t& vid, uint16_t& pid, uint16_t& mi, std::string& unique_id, |
||||||
|
std::string& device_guid) |
||||||
|
{ |
||||||
|
std::string lowerStr = symbolicLink; |
||||||
|
for (size_t i = 0; i < lowerStr.length(); i++) |
||||||
|
{ |
||||||
|
lowerStr[i] = (char)tolower(lowerStr[i]);
|
||||||
|
} |
||||||
|
auto tokens = stringSplit(lowerStr, '#'); |
||||||
|
if (tokens.size() < 1 || (tokens[0] != R"(\\?\usb)" && tokens[0] != R"(\\?\hid)")) |
||||||
|
return false; // Not a USB device
|
||||||
|
if (tokens.size() < 3) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
auto ids = stringSplit(tokens[1], '&'); |
||||||
|
if (ids[0].size() != 8 || ids[0].substr(0, 4) != "vid_" || !(std::istringstream(ids[0].substr(4, 4)) >> std::hex >> vid)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (ids[1].size() != 8 || ids[1].substr(0, 4) != "pid_" || !(std::istringstream(ids[1].substr(4, 4)) >> std::hex >> pid)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (ids.size() > 2 && (ids[2].size() != 5 || ids[2].substr(0, 3) != "mi_" || !(std::istringstream(ids[2].substr(3, 2)) >> mi))) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
ids = stringSplit(tokens[2], '&'); |
||||||
|
if (ids.size() == 0) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (ids.size() > 2) |
||||||
|
unique_id = ids[1]; |
||||||
|
else |
||||||
|
unique_id = ""; |
||||||
|
|
||||||
|
if (tokens.size() >= 3) |
||||||
|
device_guid = tokens[3]; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
#pragma pack(push, 1) |
||||||
|
template <class T> |
||||||
|
class big_endian |
||||||
|
{ |
||||||
|
T be_value; |
||||||
|
|
||||||
|
public: |
||||||
|
operator T() const |
||||||
|
{ |
||||||
|
T le_value = 0; |
||||||
|
for (unsigned int i = 0; i < sizeof(T); ++i) |
||||||
|
reinterpret_cast<char*>(&le_value)[i] = reinterpret_cast<const char*>(&be_value)[sizeof(T) - i - 1]; |
||||||
|
return le_value; |
||||||
|
} |
||||||
|
}; |
||||||
|
#pragma pack(pop) |
||||||
|
|
||||||
|
MFContext::~MFContext(void) |
||||||
|
{ |
||||||
|
CoUninitialize(); |
||||||
|
} |
||||||
|
|
||||||
|
MFContext& MFContext::getInstance() |
||||||
|
{ |
||||||
|
static MFContext instance; |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<UvcDeviceInfo> MFContext::queryUvcDeviceInfoList() |
||||||
|
{ |
||||||
|
std::vector<UvcDeviceInfo> uvcDevList; |
||||||
|
ComPtr<IMFAttributes> pAttributes = nullptr; |
||||||
|
MFCreateAttributes(&pAttributes, 1); |
||||||
|
pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); |
||||||
|
// pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY, KSCATEGORY_SENSOR_CAMERA);
|
||||||
|
IMFActivate** ppDevices; |
||||||
|
uint32_t numDevices; |
||||||
|
MFEnumDeviceSources(pAttributes.Get(), &ppDevices, &numDevices); |
||||||
|
for (uint32_t i = 0; i < numDevices; ++i) |
||||||
|
{ |
||||||
|
ComPtr<IMFActivate> pDevice; |
||||||
|
pDevice = ppDevices[i]; |
||||||
|
|
||||||
|
WCHAR* wCharStr = nullptr; |
||||||
|
uint32_t length; |
||||||
|
pDevice->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &wCharStr, &length); |
||||||
|
auto symbolicLink = wideCharToUTF8(wCharStr); |
||||||
|
CoTaskMemFree(wCharStr); |
||||||
|
|
||||||
|
pDevice->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &wCharStr, &length); |
||||||
|
auto name = wideCharToUTF8(wCharStr); |
||||||
|
CoTaskMemFree(wCharStr); |
||||||
|
|
||||||
|
uint16_t vid, pid, mi; |
||||||
|
std::string uid, guid; |
||||||
|
if (!parseUvcDeviceSymbolicLink(symbolicLink, vid, pid, mi, uid, guid)) |
||||||
|
continue; |
||||||
|
uvcDevList.emplace_back(UvcDeviceInfo({ symbolicLink, name, uid, vid, pid, mi })); |
||||||
|
CV_LOG_INFO(NULL, "UVC device found: name=" << name << ", vid=" << vid << ", pid=" << pid << ", mi=" << mi << ", uid=" << uid << ", guid=" << guid); |
||||||
|
} |
||||||
|
return uvcDevList; |
||||||
|
} |
||||||
|
|
||||||
|
Ptr<IStreamChannel> MFContext::createStreamChannel(const UvcDeviceInfo& devInfo) |
||||||
|
{ |
||||||
|
return makePtr<MSMFStreamChannel>(devInfo); |
||||||
|
} |
||||||
|
|
||||||
|
MFContext::MFContext() |
||||||
|
{ |
||||||
|
CoInitialize(0); |
||||||
|
CV_Assert(SUCCEEDED(MFStartup(MF_VERSION))); |
||||||
|
} |
||||||
|
|
||||||
|
MSMFStreamChannel::MSMFStreamChannel(const UvcDeviceInfo& devInfo) : |
||||||
|
IUvcStreamChannel(devInfo), |
||||||
|
mfContext_(MFContext::getInstance()), |
||||||
|
xuNodeId_(-1) |
||||||
|
{ |
||||||
|
HR_FAILED_RETURN(MFCreateAttributes(&deviceAttrs_, 2)); |
||||||
|
HR_FAILED_RETURN(deviceAttrs_->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)); |
||||||
|
WCHAR* buffer = new wchar_t[devInfo_.id.length() + 1]; |
||||||
|
MultiByteToWideChar(CP_UTF8, 0, devInfo_.id.c_str(), (int)devInfo_.id.length() + 1, buffer, (int)devInfo_.id.length() * sizeof(WCHAR)); |
||||||
|
HR_FAILED_EXEC(deviceAttrs_->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, buffer), { |
||||||
|
delete[] buffer; |
||||||
|
return; |
||||||
|
}) |
||||||
|
delete[] buffer; |
||||||
|
HR_FAILED_RETURN(MFCreateDeviceSource(deviceAttrs_.Get(), &deviceSource_)); |
||||||
|
HR_FAILED_RETURN(deviceSource_->QueryInterface(__uuidof(IAMCameraControl), reinterpret_cast<void**>(&cameraControl_))); |
||||||
|
HR_FAILED_RETURN(deviceSource_->QueryInterface(__uuidof(IAMVideoProcAmp), reinterpret_cast<void**>(&videoProcAmp_))); |
||||||
|
|
||||||
|
HR_FAILED_RETURN(MFCreateAttributes(&readerAttrs_, 3)); |
||||||
|
HR_FAILED_RETURN(readerAttrs_->SetUINT32(MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, false)); |
||||||
|
HR_FAILED_RETURN(readerAttrs_->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)); |
||||||
|
HR_FAILED_RETURN(readerAttrs_->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, static_cast<IUnknown*>(this))); |
||||||
|
HR_FAILED_RETURN(MFCreateSourceReaderFromMediaSource(deviceSource_.Get(), readerAttrs_.Get(), &streamReader_)); |
||||||
|
HR_FAILED_RETURN(streamReader_->SetStreamSelection(static_cast<DWORD>(MF_SOURCE_READER_ALL_STREAMS), true)); |
||||||
|
|
||||||
|
HR_FAILED_RETURN(deviceSource_->QueryInterface(__uuidof(IKsTopologyInfo), reinterpret_cast<void**>(&xuKsTopologyInfo_))); |
||||||
|
DWORD nNodes = 0; |
||||||
|
HR_FAILED_RETURN(xuKsTopologyInfo_->get_NumNodes(&nNodes)); |
||||||
|
for (DWORD i = 0; i < nNodes; i++) |
||||||
|
{ |
||||||
|
GUID nodeType; |
||||||
|
HR_FAILED_EXEC(xuKsTopologyInfo_->get_NodeType(i, &nodeType), { continue; }) |
||||||
|
if (nodeType == KSNODETYPE_DEV_SPECIFIC) |
||||||
|
{ |
||||||
|
xuNodeId_ = i; |
||||||
|
} |
||||||
|
} |
||||||
|
if (xuNodeId_ != -1) |
||||||
|
{ |
||||||
|
HR_FAILED_RETURN(xuKsTopologyInfo_->CreateNodeInstance(xuNodeId_, IID_IUnknown, reinterpret_cast<LPVOID*>(&xuNodeInstance_))); |
||||||
|
HR_FAILED_RETURN(xuNodeInstance_->QueryInterface(__uuidof(IKsControl), reinterpret_cast<void**>(&xuKsControl_))); |
||||||
|
} |
||||||
|
|
||||||
|
if (streamType_ == OBSENSOR_STREAM_DEPTH) |
||||||
|
{ |
||||||
|
initDepthFrameProcessor(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MSMFStreamChannel::~MSMFStreamChannel() |
||||||
|
{ |
||||||
|
stop(); |
||||||
|
if (cameraControl_) |
||||||
|
{ |
||||||
|
cameraControl_.Release(); |
||||||
|
} |
||||||
|
if (videoProcAmp_) |
||||||
|
{ |
||||||
|
videoProcAmp_.Release(); |
||||||
|
} |
||||||
|
if (streamReader_) |
||||||
|
{ |
||||||
|
streamReader_.Release(); |
||||||
|
} |
||||||
|
if (readerAttrs_) |
||||||
|
{ |
||||||
|
readerAttrs_.Release(); |
||||||
|
} |
||||||
|
if (deviceAttrs_) |
||||||
|
{ |
||||||
|
deviceAttrs_.Release(); |
||||||
|
} |
||||||
|
if (deviceSource_) |
||||||
|
{ |
||||||
|
deviceSource_.Release(); |
||||||
|
} |
||||||
|
if (xuKsTopologyInfo_) |
||||||
|
{ |
||||||
|
xuKsTopologyInfo_.Release(); |
||||||
|
} |
||||||
|
if (xuNodeInstance_) |
||||||
|
{ |
||||||
|
xuNodeInstance_.Release(); |
||||||
|
} |
||||||
|
if (xuKsControl_) |
||||||
|
{ |
||||||
|
xuKsControl_.Release(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void MSMFStreamChannel::start(const StreamProfile& profile, FrameCallback frameCallback) |
||||||
|
{ |
||||||
|
ComPtr<IMFMediaType> mediaType = nullptr; |
||||||
|
unsigned int width, height, fps; |
||||||
|
FrameRate frameRateMin, frameRateMax; |
||||||
|
bool quit = false; |
||||||
|
|
||||||
|
frameCallback_ = frameCallback; |
||||||
|
currentProfile_ = profile; |
||||||
|
currentStreamIndex_ = -1; |
||||||
|
|
||||||
|
for (uint8_t index = 0; index <= 5; index++) |
||||||
|
{ |
||||||
|
for (uint32_t k = 0;; k++) |
||||||
|
{ |
||||||
|
HR_FAILED_EXEC(streamReader_->GetNativeMediaType(index, k, &mediaType), { continue; }) |
||||||
|
GUID subtype; |
||||||
|
HR_FAILED_RETURN(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype)); |
||||||
|
HR_FAILED_RETURN(MFGetAttributeSize(mediaType.Get(), MF_MT_FRAME_SIZE, &width, &height)); |
||||||
|
HR_FAILED_RETURN(MFGetAttributeRatio(mediaType.Get(), MF_MT_FRAME_RATE_RANGE_MIN, &frameRateMin.numerator, &frameRateMin.denominator)); |
||||||
|
HR_FAILED_RETURN(MFGetAttributeRatio(mediaType.Get(), MF_MT_FRAME_RATE_RANGE_MAX, &frameRateMax.numerator, &frameRateMax.denominator)); |
||||||
|
|
||||||
|
if (static_cast<float>(frameRateMax.numerator) / frameRateMax.denominator < static_cast<float>(frameRateMin.numerator) / frameRateMin.denominator) |
||||||
|
{ |
||||||
|
std::swap(frameRateMax, frameRateMin); |
||||||
|
} |
||||||
|
|
||||||
|
fps = frameRateMax.numerator / frameRateMax.denominator; |
||||||
|
uint32_t device_fourcc = reinterpret_cast<const big_endian<uint32_t> &>(subtype.Data1); |
||||||
|
if (width == profile.width && |
||||||
|
height == profile.height && |
||||||
|
fps == profile.fps && |
||||||
|
frameFourccToFormat(device_fourcc) == profile.format) |
||||||
|
{ |
||||||
|
HR_FAILED_RETURN(streamReader_->SetCurrentMediaType(index, nullptr, mediaType.Get())); |
||||||
|
HR_FAILED_RETURN(streamReader_->SetStreamSelection(index, true)); |
||||||
|
streamReader_->ReadSample(index, 0, nullptr, nullptr, nullptr, nullptr); |
||||||
|
|
||||||
|
streamState_ = STREAM_STARTING; |
||||||
|
currentStreamIndex_ = index; |
||||||
|
quit = true; |
||||||
|
|
||||||
|
// wait for frame
|
||||||
|
std::unique_lock<std::mutex> lock(streamStateMutex_); |
||||||
|
auto success = streamStateCv_.wait_for(lock, std::chrono::milliseconds(3000), [&]() { |
||||||
|
return streamState_ == STREAM_STARTED; |
||||||
|
}); |
||||||
|
if (!success) |
||||||
|
{ |
||||||
|
stop(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
mediaType.Release(); |
||||||
|
} |
||||||
|
if (quit) |
||||||
|
{ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
streamState_ = quit ? streamState_ : STREAM_STOPED; |
||||||
|
} |
||||||
|
|
||||||
|
void MSMFStreamChannel::stop() |
||||||
|
{ |
||||||
|
if (streamState_ == STREAM_STARTING || streamState_ == STREAM_STARTED) |
||||||
|
{ |
||||||
|
streamState_ = STREAM_STOPPING; |
||||||
|
streamReader_->SetStreamSelection(currentStreamIndex_, false); |
||||||
|
streamReader_->Flush(currentStreamIndex_); |
||||||
|
std::unique_lock<std::mutex> lk(streamStateMutex_); |
||||||
|
streamStateCv_.wait_for(lk, std::chrono::milliseconds(1000), [&]() { |
||||||
|
return streamState_ == STREAM_STOPED; |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool MSMFStreamChannel::setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) |
||||||
|
{ |
||||||
|
if (xuSendBuf_.size() < XU_MAX_DATA_LENGTH) { |
||||||
|
xuSendBuf_.resize(XU_MAX_DATA_LENGTH); |
||||||
|
} |
||||||
|
memcpy(xuSendBuf_.data(), data, len); |
||||||
|
|
||||||
|
KSP_NODE node; |
||||||
|
memset(&node, 0, sizeof(KSP_NODE)); |
||||||
|
node.Property.Set = { 0xA55751A1, 0xF3C5, 0x4A5E, {0x8D, 0x5A, 0x68, 0x54, 0xB8, 0xFA, 0x27, 0x16} }; |
||||||
|
node.Property.Id = ctrl; |
||||||
|
node.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY; |
||||||
|
node.NodeId = xuNodeId_; |
||||||
|
|
||||||
|
ULONG bytes_received = 0; |
||||||
|
HR_FAILED_EXEC(xuKsControl_->KsProperty(reinterpret_cast<PKSPROPERTY>(&node), sizeof(KSP_NODE), (void*)xuSendBuf_.data(), XU_MAX_DATA_LENGTH, &bytes_received), { |
||||||
|
return false; |
||||||
|
}); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool MSMFStreamChannel::getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) |
||||||
|
{ |
||||||
|
if (xuRecvBuf_.size() < XU_MAX_DATA_LENGTH) { |
||||||
|
xuRecvBuf_.resize(XU_MAX_DATA_LENGTH); |
||||||
|
} |
||||||
|
KSP_NODE node; |
||||||
|
memset(&node, 0, sizeof(KSP_NODE)); |
||||||
|
node.Property.Set = { 0xA55751A1, 0xF3C5, 0x4A5E, {0x8D, 0x5A, 0x68, 0x54, 0xB8, 0xFA, 0x27, 0x16} }; |
||||||
|
node.Property.Id = ctrl; |
||||||
|
node.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY; |
||||||
|
node.NodeId = xuNodeId_; |
||||||
|
|
||||||
|
ULONG bytes_received = 0; |
||||||
|
HR_FAILED_EXEC(xuKsControl_->KsProperty(reinterpret_cast<PKSPROPERTY>(&node), sizeof(node), xuRecvBuf_.data(), XU_MAX_DATA_LENGTH, &bytes_received), { |
||||||
|
*len = 0; |
||||||
|
data = nullptr; |
||||||
|
return false; |
||||||
|
}); |
||||||
|
*data = xuRecvBuf_.data(); |
||||||
|
*len = bytes_received; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP MSMFStreamChannel::QueryInterface(REFIID iid, void** ppv) |
||||||
|
{ |
||||||
|
#pragma warning(push) |
||||||
|
#pragma warning(disable : 4838) |
||||||
|
static const QITAB qit[] = { |
||||||
|
QITABENT(MSMFStreamChannel, IMFSourceReaderCallback), |
||||||
|
{nullptr}, |
||||||
|
}; |
||||||
|
return QISearch(this, qit, iid, ppv); |
||||||
|
#pragma warning(pop) |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG) |
||||||
|
MSMFStreamChannel::AddRef() |
||||||
|
{ |
||||||
|
return InterlockedIncrement(&refCount_); |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG) |
||||||
|
MSMFStreamChannel::Release() |
||||||
|
{ |
||||||
|
ULONG count = InterlockedDecrement(&refCount_); |
||||||
|
if (count <= 0) |
||||||
|
{ |
||||||
|
delete this; |
||||||
|
} |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP MSMFStreamChannel::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD /*dwStreamFlags*/, LONGLONG /*timeStamp*/, IMFSample* sample) |
||||||
|
{ |
||||||
|
HR_FAILED_LOG(hrStatus); |
||||||
|
|
||||||
|
if (streamState_ == STREAM_STARTING) |
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> lock(streamStateMutex_); |
||||||
|
streamState_ = STREAM_STARTED; |
||||||
|
streamStateCv_.notify_all(); |
||||||
|
} |
||||||
|
|
||||||
|
if (streamState_ != STREAM_STOPPING && streamState_ != STREAM_STOPED) |
||||||
|
{ |
||||||
|
HR_FAILED_LOG(streamReader_->ReadSample(dwStreamIndex, 0, nullptr, nullptr, nullptr, nullptr)); |
||||||
|
if (sample) |
||||||
|
{ |
||||||
|
ComPtr<IMFMediaBuffer> buffer = nullptr; |
||||||
|
DWORD max_length, current_length; |
||||||
|
byte* byte_buffer = nullptr; |
||||||
|
|
||||||
|
HR_FAILED_EXEC(sample->GetBufferByIndex(0, &buffer), { return S_OK; }); |
||||||
|
|
||||||
|
buffer->Lock(&byte_buffer, &max_length, ¤t_length); |
||||||
|
Frame fo = { currentProfile_.format, currentProfile_.width, currentProfile_.height, current_length, (uint8_t*)byte_buffer }; |
||||||
|
if (depthFrameProcessor_) |
||||||
|
{ |
||||||
|
depthFrameProcessor_->process(&fo); |
||||||
|
} |
||||||
|
frameCallback_(&fo); |
||||||
|
buffer->Unlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
return S_OK; |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP MSMFStreamChannel::OnEvent(DWORD /*sidx*/, IMFMediaEvent* /*event*/) |
||||||
|
{ |
||||||
|
return S_OK; |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP MSMFStreamChannel::OnFlush(DWORD) |
||||||
|
{ |
||||||
|
if (streamState_ == STREAM_STARTING) |
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> lock(streamStateMutex_); |
||||||
|
streamState_ = STREAM_STOPED; |
||||||
|
streamStateCv_.notify_all(); |
||||||
|
} |
||||||
|
return S_OK; |
||||||
|
} |
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR_MSMF
|
@ -0,0 +1,180 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_MSMF_HPP |
||||||
|
#define OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_MSMF_HPP |
||||||
|
#ifdef HAVE_OBSENSOR_MSMF |
||||||
|
|
||||||
|
#include "obsensor_uvc_stream_channel.hpp" |
||||||
|
|
||||||
|
#include <windows.h> |
||||||
|
#include <guiddef.h> |
||||||
|
#include <mfapi.h> |
||||||
|
#include <mfidl.h> |
||||||
|
#include <mfplay.h> |
||||||
|
#include <mfobjects.h> |
||||||
|
#include <mfreadwrite.h> |
||||||
|
#include <tchar.h> |
||||||
|
#include <strsafe.h> |
||||||
|
#include <codecvt> |
||||||
|
#include <ks.h> |
||||||
|
#include <comdef.h> |
||||||
|
#include <mutex> |
||||||
|
#include <vidcap.h> //IKsTopologyInfo |
||||||
|
#include <ksproxy.h> //IKsControl |
||||||
|
#include <ksmedia.h> |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
template <class T> |
||||||
|
class ComPtr |
||||||
|
{ |
||||||
|
public: |
||||||
|
ComPtr() {} |
||||||
|
ComPtr(T* lp) |
||||||
|
{ |
||||||
|
p = lp; |
||||||
|
} |
||||||
|
ComPtr(_In_ const ComPtr<T>& lp) |
||||||
|
{ |
||||||
|
p = lp.p; |
||||||
|
} |
||||||
|
virtual ~ComPtr() {} |
||||||
|
|
||||||
|
void swap(_In_ ComPtr<T>& lp) |
||||||
|
{ |
||||||
|
ComPtr<T> tmp(p); |
||||||
|
p = lp.p; |
||||||
|
lp.p = tmp.p; |
||||||
|
tmp = NULL; |
||||||
|
} |
||||||
|
T** operator&() |
||||||
|
{ |
||||||
|
CV_Assert(p == NULL); |
||||||
|
return p.operator&(); |
||||||
|
} |
||||||
|
T* operator->() const |
||||||
|
{ |
||||||
|
CV_Assert(p != NULL); |
||||||
|
return p.operator->(); |
||||||
|
} |
||||||
|
operator bool() |
||||||
|
{ |
||||||
|
return p.operator!=(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
T* Get() const |
||||||
|
{ |
||||||
|
return p; |
||||||
|
} |
||||||
|
|
||||||
|
void Release() |
||||||
|
{ |
||||||
|
if (p) |
||||||
|
p.Release(); |
||||||
|
} |
||||||
|
|
||||||
|
// query for U interface
|
||||||
|
template <typename U> |
||||||
|
HRESULT As(_Out_ ComPtr<U>& lp) const |
||||||
|
{ |
||||||
|
lp.Release(); |
||||||
|
return p->QueryInterface(__uuidof(U), reinterpret_cast<void**>((T**)&lp)); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
_COM_SMARTPTR_TYPEDEF(T, __uuidof(T)); |
||||||
|
TPtr p; |
||||||
|
}; |
||||||
|
|
||||||
|
class MFContext |
||||||
|
{ |
||||||
|
public: |
||||||
|
~MFContext(void); |
||||||
|
static MFContext& getInstance(); |
||||||
|
|
||||||
|
std::vector<UvcDeviceInfo> queryUvcDeviceInfoList(); |
||||||
|
Ptr<IStreamChannel> createStreamChannel(const UvcDeviceInfo& devInfo); |
||||||
|
|
||||||
|
private: |
||||||
|
MFContext(void); |
||||||
|
}; |
||||||
|
|
||||||
|
struct FrameRate |
||||||
|
{ |
||||||
|
unsigned int denominator; |
||||||
|
unsigned int numerator; |
||||||
|
}; |
||||||
|
|
||||||
|
class MSMFStreamChannel : public IUvcStreamChannel, public IMFSourceReaderCallback |
||||||
|
{ |
||||||
|
public: |
||||||
|
MSMFStreamChannel(const UvcDeviceInfo& devInfo); |
||||||
|
virtual ~MSMFStreamChannel() noexcept; |
||||||
|
|
||||||
|
virtual void start(const StreamProfile& profile, FrameCallback frameCallback) override; |
||||||
|
virtual void stop() override; |
||||||
|
|
||||||
|
private: |
||||||
|
virtual bool setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) override; |
||||||
|
virtual bool getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) override; |
||||||
|
|
||||||
|
private: |
||||||
|
MFContext& mfContext_; |
||||||
|
|
||||||
|
ComPtr<IMFAttributes> deviceAttrs_ = nullptr; |
||||||
|
ComPtr<IMFMediaSource> deviceSource_ = nullptr; |
||||||
|
ComPtr<IMFAttributes> readerAttrs_ = nullptr; |
||||||
|
ComPtr<IMFSourceReader> streamReader_ = nullptr; |
||||||
|
ComPtr<IAMCameraControl> cameraControl_ = nullptr; |
||||||
|
ComPtr<IAMVideoProcAmp> videoProcAmp_ = nullptr; |
||||||
|
ComPtr<IKsTopologyInfo> xuKsTopologyInfo_ = nullptr; |
||||||
|
ComPtr<IUnknown> xuNodeInstance_ = nullptr; |
||||||
|
ComPtr<IKsControl> xuKsControl_ = nullptr; |
||||||
|
int xuNodeId_; |
||||||
|
|
||||||
|
FrameCallback frameCallback_; |
||||||
|
StreamProfile currentProfile_; |
||||||
|
int8_t currentStreamIndex_; |
||||||
|
|
||||||
|
StreamState streamState_; |
||||||
|
std::mutex streamStateMutex_; |
||||||
|
std::condition_variable streamStateCv_; |
||||||
|
|
||||||
|
std::vector<uint8_t> xuRecvBuf_; |
||||||
|
std::vector<uint8_t> xuSendBuf_; |
||||||
|
|
||||||
|
public: |
||||||
|
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; |
||||||
|
STDMETHODIMP_(ULONG) |
||||||
|
AddRef() override; |
||||||
|
STDMETHODIMP_(ULONG) |
||||||
|
Release() override; |
||||||
|
STDMETHODIMP OnReadSample(HRESULT /*hrStatus*/, DWORD dwStreamIndex, DWORD /*dwStreamFlags*/, LONGLONG /*llTimestamp*/, IMFSample* sample) override; |
||||||
|
STDMETHODIMP OnEvent(DWORD /*sidx*/, IMFMediaEvent* /*event*/) override; |
||||||
|
STDMETHODIMP OnFlush(DWORD) override; |
||||||
|
|
||||||
|
private: |
||||||
|
long refCount_ = 1; |
||||||
|
}; |
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR_MSMF
|
||||||
|
#endif // OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_MSMF_HPP
|
@ -0,0 +1,379 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef HAVE_OBSENSOR_V4L2 |
||||||
|
#include "obsensor_stream_channel_v4l2.hpp" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <sys/ioctl.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/mman.h> |
||||||
|
#include <linux/videodev2.h> |
||||||
|
#include <linux/uvcvideo.h> |
||||||
|
#include <linux/usb/video.h> |
||||||
|
#include <fstream> |
||||||
|
#include <map> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "opencv2/core/utils/filesystem.hpp" |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
|
||||||
|
#define IOCTL_FAILED_RETURN(x) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_WARNING(NULL, "ioctl error return: " << errno); \
|
||||||
|
return; \
|
||||||
|
} |
||||||
|
|
||||||
|
#define IOCTL_FAILED_LOG(x) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_WARNING(NULL, "ioctl error return: " << errno); \
|
||||||
|
} |
||||||
|
|
||||||
|
#define IOCTL_FAILED_CONTINUE(x) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_WARNING(NULL, "ioctl error return: " << errno); \
|
||||||
|
continue; \
|
||||||
|
} |
||||||
|
|
||||||
|
#define IOCTL_FAILED_EXEC(x, statement) \ |
||||||
|
if (x < 0) \
|
||||||
|
{ \
|
||||||
|
CV_LOG_WARNING(NULL, "ioctl error return: " << errno); \
|
||||||
|
statement; \
|
||||||
|
} |
||||||
|
|
||||||
|
int xioctl(int fd, int req, void* arg) |
||||||
|
{ |
||||||
|
int rst; |
||||||
|
int retry = 5; |
||||||
|
do |
||||||
|
{ |
||||||
|
rst = ioctl(fd, req, arg); |
||||||
|
retry--; |
||||||
|
} while (rst == -1 && (errno == EAGAIN || (errno == EBUSY && retry > 0))); |
||||||
|
|
||||||
|
if (rst < 0) |
||||||
|
{ |
||||||
|
CV_LOG_WARNING(NULL, "ioctl: fd=" << fd << ", req=" << req); |
||||||
|
} |
||||||
|
return rst; |
||||||
|
} |
||||||
|
|
||||||
|
V4L2Context& V4L2Context::getInstance() |
||||||
|
{ |
||||||
|
static V4L2Context instance; |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<UvcDeviceInfo> V4L2Context::queryUvcDeviceInfoList() |
||||||
|
{ |
||||||
|
std::vector<UvcDeviceInfo> uvcDevList; |
||||||
|
std::map<std::string, UvcDeviceInfo> uvcDevMap; |
||||||
|
const cv::String videosDir = "/sys/class/video4linux"; |
||||||
|
cv::utils::Paths videos; |
||||||
|
if (cv::utils::fs::isDirectory(videosDir)) |
||||||
|
{ |
||||||
|
cv::utils::fs::glob(videosDir, "*", videos, false, true); |
||||||
|
for (const auto& video : videos) |
||||||
|
{ |
||||||
|
UvcDeviceInfo uvcDev; |
||||||
|
cv::String videoName = video.substr(video.find_last_of("/") + 1); |
||||||
|
char buf[PATH_MAX]; |
||||||
|
if (realpath(video.c_str(), buf) == nullptr || cv::String(buf).find("virtual") != std::string::npos) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
cv::String videoRealPath = buf; |
||||||
|
cv::String interfaceRealPath = videoRealPath.substr(0, videoRealPath.find_last_of("/")); |
||||||
|
|
||||||
|
std::string busNum, devNum, devPath; |
||||||
|
while (videoRealPath.find_last_of("/") != std::string::npos) |
||||||
|
{ |
||||||
|
videoRealPath = videoRealPath.substr(0, videoRealPath.find_last_of("/")); |
||||||
|
if (!(std::ifstream(videoRealPath + "/busnum") >> busNum)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (!(std::ifstream(videoRealPath + "/devpath") >> devPath)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (!(std::ifstream(videoRealPath + "/devnum") >> devNum)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
uvcDev.uid = busNum + "-" + devPath + "-" + devNum; |
||||||
|
break; |
||||||
|
/* code */ |
||||||
|
} |
||||||
|
|
||||||
|
uvcDev.id = cv::String("/dev/") + videoName; |
||||||
|
v4l2_capability caps = {}; |
||||||
|
int videoFd = open(uvcDev.id.c_str(), O_RDONLY); |
||||||
|
IOCTL_FAILED_EXEC(xioctl(videoFd, VIDIOC_QUERYCAP, &caps), { |
||||||
|
close(videoFd); |
||||||
|
continue; |
||||||
|
}); |
||||||
|
close(videoFd); |
||||||
|
|
||||||
|
if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) |
||||||
|
{ |
||||||
|
cv::String modalias; |
||||||
|
if (!(std::ifstream(video + "/device/modalias") >> modalias) || |
||||||
|
modalias.size() < 14 || |
||||||
|
modalias.substr(0, 5) != "usb:v" || |
||||||
|
modalias[9] != 'p') |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
std::istringstream(modalias.substr(5, 4)) >> std::hex >> uvcDev.vid; |
||||||
|
std::istringstream(modalias.substr(10, 4)) >> std::hex >> uvcDev.pid; |
||||||
|
std::getline(std::ifstream(video + "/device/interface"), uvcDev.name); |
||||||
|
std::ifstream(video + "/device/bInterfaceNumber") >> uvcDev.mi; |
||||||
|
uvcDevMap.insert({ interfaceRealPath, uvcDev }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
for (const auto& item : uvcDevMap) |
||||||
|
{ |
||||||
|
const auto uvcDev = item.second; // alias
|
||||||
|
CV_LOG_INFO(NULL, "UVC device found: name=" << uvcDev.name << ", vid=" << uvcDev.vid << ", pid=" << uvcDev.pid << ", mi=" << uvcDev.mi << ", uid=" << uvcDev.uid << ", id=" << uvcDev.id); |
||||||
|
uvcDevList.push_back(uvcDev); |
||||||
|
} |
||||||
|
return uvcDevList; |
||||||
|
} |
||||||
|
|
||||||
|
Ptr<IStreamChannel> V4L2Context::createStreamChannel(const UvcDeviceInfo& devInfo) |
||||||
|
{ |
||||||
|
return makePtr<V4L2StreamChannel>(devInfo); |
||||||
|
} |
||||||
|
|
||||||
|
V4L2StreamChannel::V4L2StreamChannel(const UvcDeviceInfo &devInfo) : IUvcStreamChannel(devInfo), |
||||||
|
devFd_(-1), |
||||||
|
streamState_(STREAM_STOPED) |
||||||
|
{ |
||||||
|
|
||||||
|
devFd_ = open(devInfo_.id.c_str(), O_RDWR | O_NONBLOCK, 0); |
||||||
|
if (devFd_ < 0) |
||||||
|
{ |
||||||
|
CV_LOG_ERROR(NULL, "Open " << devInfo_.id << " failed ! errno=" << errno) |
||||||
|
} |
||||||
|
else if (streamType_ == OBSENSOR_STREAM_DEPTH) |
||||||
|
{ |
||||||
|
initDepthFrameProcessor(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
V4L2StreamChannel::~V4L2StreamChannel() noexcept |
||||||
|
{ |
||||||
|
stop(); |
||||||
|
if (devFd_) |
||||||
|
{ |
||||||
|
close(devFd_); |
||||||
|
devFd_ = -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void V4L2StreamChannel::start(const StreamProfile& profile, FrameCallback frameCallback) |
||||||
|
{ |
||||||
|
if (streamState_ != STREAM_STOPED) |
||||||
|
{ |
||||||
|
CV_LOG_ERROR(NULL, devInfo_.id << ": repetitive operation!") |
||||||
|
return; |
||||||
|
} |
||||||
|
frameCallback_ = frameCallback; |
||||||
|
currentProfile_ = profile; |
||||||
|
|
||||||
|
struct v4l2_format fmt = {}; |
||||||
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
fmt.fmt.pix.width = profile.width; |
||||||
|
fmt.fmt.pix.height = profile.height; |
||||||
|
fmt.fmt.pix.pixelformat = frameFormatToFourcc(profile.format); |
||||||
|
IOCTL_FAILED_RETURN(xioctl(devFd_, VIDIOC_S_FMT, &fmt)); |
||||||
|
IOCTL_FAILED_RETURN(xioctl(devFd_, VIDIOC_G_FMT, &fmt)); |
||||||
|
|
||||||
|
struct v4l2_streamparm streamParm = {}; |
||||||
|
streamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
streamParm.parm.capture.timeperframe.numerator = 1; |
||||||
|
streamParm.parm.capture.timeperframe.denominator = profile.fps; |
||||||
|
IOCTL_FAILED_RETURN(xioctl(devFd_, VIDIOC_S_PARM, &streamParm)); |
||||||
|
IOCTL_FAILED_RETURN(xioctl(devFd_, VIDIOC_G_PARM, &streamParm)); |
||||||
|
|
||||||
|
struct v4l2_requestbuffers req = {}; |
||||||
|
req.count = MAX_FRAME_BUFFER_NUM; |
||||||
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
req.memory = V4L2_MEMORY_MMAP; |
||||||
|
IOCTL_FAILED_RETURN(xioctl(devFd_, VIDIOC_REQBUFS, &req)); |
||||||
|
|
||||||
|
for (uint32_t i = 0; i < req.count && i < MAX_FRAME_BUFFER_NUM; i++) |
||||||
|
{ |
||||||
|
struct v4l2_buffer buf = {}; |
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
buf.memory = V4L2_MEMORY_MMAP; |
||||||
|
buf.index = i; // only one buffer
|
||||||
|
IOCTL_FAILED_RETURN(xioctl(devFd_, VIDIOC_QUERYBUF, &buf)); |
||||||
|
frameBuffList[i].ptr = (uint8_t*)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, devFd_, buf.m.offset); |
||||||
|
frameBuffList[i].length = buf.length; |
||||||
|
} |
||||||
|
|
||||||
|
// stream on
|
||||||
|
std::unique_lock<std::mutex> lk(streamStateMutex_); |
||||||
|
streamState_ = STREAM_STARTING; |
||||||
|
uint32_t type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
IOCTL_FAILED_EXEC(xioctl(devFd_, VIDIOC_STREAMON, &type), { |
||||||
|
streamState_ = STREAM_STOPED; |
||||||
|
for (uint32_t i = 0; i < MAX_FRAME_BUFFER_NUM; i++) |
||||||
|
{ |
||||||
|
if (frameBuffList[i].ptr) |
||||||
|
{ |
||||||
|
munmap(frameBuffList[i].ptr, frameBuffList[i].length); |
||||||
|
frameBuffList[i].ptr = nullptr; |
||||||
|
frameBuffList[i].length = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
return; |
||||||
|
}); |
||||||
|
grabFrameThread_ = std::thread(&V4L2StreamChannel::grabFrame, this); |
||||||
|
} |
||||||
|
|
||||||
|
void V4L2StreamChannel::grabFrame() |
||||||
|
{ |
||||||
|
fd_set fds; |
||||||
|
FD_ZERO(&fds); |
||||||
|
FD_SET(devFd_, &fds); |
||||||
|
struct timeval tv = {}; |
||||||
|
tv.tv_sec = 0; |
||||||
|
tv.tv_usec = 100000; // 100ms
|
||||||
|
|
||||||
|
struct v4l2_buffer buf = {}; |
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
buf.memory = V4L2_MEMORY_MMAP; |
||||||
|
// buf.index = 0;
|
||||||
|
|
||||||
|
IOCTL_FAILED_EXEC(xioctl(devFd_, VIDIOC_QBUF, &buf), { |
||||||
|
std::unique_lock<std::mutex> lk(streamStateMutex_); |
||||||
|
streamState_ = STREAM_STOPED; |
||||||
|
streamStateCv_.notify_all(); |
||||||
|
return; |
||||||
|
}); |
||||||
|
|
||||||
|
while (streamState_ == STREAM_STARTING || streamState_ == STREAM_STARTED) |
||||||
|
{ |
||||||
|
IOCTL_FAILED_CONTINUE(select(devFd_ + 1, &fds, NULL, NULL, &tv)); |
||||||
|
IOCTL_FAILED_CONTINUE(xioctl(devFd_, VIDIOC_DQBUF, &buf)); |
||||||
|
if (streamState_ == STREAM_STARTING) |
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> lk(streamStateMutex_); |
||||||
|
streamState_ = STREAM_STARTED; |
||||||
|
streamStateCv_.notify_all(); |
||||||
|
} |
||||||
|
Frame fo = { currentProfile_.format, currentProfile_.width, currentProfile_.height, buf.length, frameBuffList[buf.index].ptr }; |
||||||
|
if (depthFrameProcessor_) |
||||||
|
{ |
||||||
|
depthFrameProcessor_->process(&fo); |
||||||
|
} |
||||||
|
frameCallback_(&fo); |
||||||
|
IOCTL_FAILED_CONTINUE(xioctl(devFd_, VIDIOC_QBUF, &buf)); |
||||||
|
} |
||||||
|
std::unique_lock<std::mutex> lk(streamStateMutex_); |
||||||
|
streamState_ = STREAM_STOPED; |
||||||
|
streamStateCv_.notify_all(); |
||||||
|
} |
||||||
|
|
||||||
|
bool V4L2StreamChannel::setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) |
||||||
|
{ |
||||||
|
if (xuSendBuf_.size() < XU_MAX_DATA_LENGTH) { |
||||||
|
xuSendBuf_.resize(XU_MAX_DATA_LENGTH); |
||||||
|
} |
||||||
|
memcpy(xuSendBuf_.data(), data, len); |
||||||
|
struct uvc_xu_control_query xu_ctrl_query = { |
||||||
|
.unit = XU_UNIT_ID, |
||||||
|
.selector = ctrl, |
||||||
|
.query = UVC_SET_CUR, |
||||||
|
.size = (__u16)(ctrl == 1 ? 512 : (ctrl == 2 ? 64 : 1024)), |
||||||
|
.data = xuSendBuf_.data() |
||||||
|
}; |
||||||
|
if (devFd_ > 0) |
||||||
|
{ |
||||||
|
IOCTL_FAILED_EXEC(xioctl(devFd_, UVCIOC_CTRL_QUERY, &xu_ctrl_query), { return false; }); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool V4L2StreamChannel::getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) |
||||||
|
{ |
||||||
|
if (xuRecvBuf_.size() < XU_MAX_DATA_LENGTH) { |
||||||
|
xuRecvBuf_.resize(XU_MAX_DATA_LENGTH); |
||||||
|
} |
||||||
|
struct uvc_xu_control_query xu_ctrl_query = { |
||||||
|
.unit = XU_UNIT_ID, |
||||||
|
.selector = ctrl, |
||||||
|
.query = UVC_GET_CUR, |
||||||
|
.size = (__u16)(ctrl == 1 ? 512 : (ctrl == 2 ? 64 : 1024)), |
||||||
|
.data = xuRecvBuf_.data() |
||||||
|
}; |
||||||
|
|
||||||
|
IOCTL_FAILED_EXEC(xioctl(devFd_, UVCIOC_CTRL_QUERY, &xu_ctrl_query), { |
||||||
|
*len = 0; |
||||||
|
return false; |
||||||
|
}); |
||||||
|
|
||||||
|
*len = xu_ctrl_query.size; |
||||||
|
*data = xuRecvBuf_.data(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void V4L2StreamChannel::stop() |
||||||
|
{ |
||||||
|
if (streamState_ == STREAM_STARTING || streamState_ == STREAM_STARTED) |
||||||
|
{ |
||||||
|
streamState_ = STREAM_STOPPING; |
||||||
|
std::unique_lock<std::mutex> lk(streamStateMutex_); |
||||||
|
streamStateCv_.wait_for(lk, std::chrono::milliseconds(1000), [&](){ |
||||||
|
return streamState_ == STREAM_STOPED; |
||||||
|
}); |
||||||
|
uint32_t type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
||||||
|
IOCTL_FAILED_LOG(xioctl(devFd_, VIDIOC_STREAMOFF, &type)); |
||||||
|
} |
||||||
|
if (grabFrameThread_.joinable()) |
||||||
|
{ |
||||||
|
grabFrameThread_.join(); |
||||||
|
} |
||||||
|
for (uint32_t i = 0; i < MAX_FRAME_BUFFER_NUM; i++) |
||||||
|
{ |
||||||
|
if (frameBuffList[i].ptr) |
||||||
|
{ |
||||||
|
munmap(frameBuffList[i].ptr, frameBuffList[i].length); |
||||||
|
frameBuffList[i].ptr = nullptr; |
||||||
|
frameBuffList[i].length = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR_V4L2
|
@ -0,0 +1,90 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_V4L2_HPP |
||||||
|
#define OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_V4L2_HPP |
||||||
|
#ifdef HAVE_OBSENSOR_V4L2 |
||||||
|
|
||||||
|
#include "obsensor_uvc_stream_channel.hpp" |
||||||
|
|
||||||
|
#include <mutex> |
||||||
|
#include <condition_variable> |
||||||
|
#include <thread> |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
#define MAX_FRAME_BUFFER_NUM 4 |
||||||
|
struct V4L2FrameBuffer |
||||||
|
{ |
||||||
|
uint32_t length = 0; |
||||||
|
uint8_t* ptr = nullptr; |
||||||
|
}; |
||||||
|
|
||||||
|
int xioctl(int fd, int req, void* arg); |
||||||
|
|
||||||
|
class V4L2Context |
||||||
|
{ |
||||||
|
public: |
||||||
|
~V4L2Context() {} |
||||||
|
static V4L2Context& getInstance(); |
||||||
|
|
||||||
|
std::vector<UvcDeviceInfo> queryUvcDeviceInfoList(); |
||||||
|
Ptr<IStreamChannel> createStreamChannel(const UvcDeviceInfo& devInfo); |
||||||
|
|
||||||
|
private: |
||||||
|
V4L2Context() noexcept {} |
||||||
|
}; |
||||||
|
|
||||||
|
class V4L2StreamChannel : public IUvcStreamChannel |
||||||
|
{ |
||||||
|
public: |
||||||
|
V4L2StreamChannel(const UvcDeviceInfo& devInfo); |
||||||
|
virtual ~V4L2StreamChannel() noexcept; |
||||||
|
|
||||||
|
virtual void start(const StreamProfile& profile, FrameCallback frameCallback) override; |
||||||
|
virtual void stop() override; |
||||||
|
|
||||||
|
private: |
||||||
|
void grabFrame(); |
||||||
|
|
||||||
|
virtual bool setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) override; |
||||||
|
virtual bool getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) override; |
||||||
|
|
||||||
|
private: |
||||||
|
int devFd_; |
||||||
|
|
||||||
|
V4L2FrameBuffer frameBuffList[MAX_FRAME_BUFFER_NUM]; |
||||||
|
|
||||||
|
StreamState streamState_; |
||||||
|
std::mutex streamStateMutex_; |
||||||
|
std::condition_variable streamStateCv_; |
||||||
|
|
||||||
|
std::thread grabFrameThread_; |
||||||
|
|
||||||
|
FrameCallback frameCallback_; |
||||||
|
StreamProfile currentProfile_; |
||||||
|
|
||||||
|
std::vector<uint8_t> xuRecvBuf_; |
||||||
|
std::vector<uint8_t> xuSendBuf_; |
||||||
|
}; |
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR_V4L2
|
||||||
|
#endif // OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_V4L2_HPP
|
@ -0,0 +1,265 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#if defined(HAVE_OBSENSOR_V4L2) || defined(HAVE_OBSENSOR_MSMF) |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <vector> |
||||||
|
#include <string> |
||||||
|
#include <algorithm> |
||||||
|
#include <iterator> |
||||||
|
|
||||||
|
#if defined(HAVE_OBSENSOR_V4L2) |
||||||
|
#include "obsensor_stream_channel_v4l2.hpp" |
||||||
|
#elif defined(HAVE_OBSENSOR_MSMF) |
||||||
|
#include "obsensor_stream_channel_msmf.hpp" |
||||||
|
#endif // HAVE_OBSENSOR_V4L2
|
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
const uint8_t OB_EXT_CMD0[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x52, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; |
||||||
|
const uint8_t OB_EXT_CMD1[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x54, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
||||||
|
const uint8_t OB_EXT_CMD2[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x56, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; |
||||||
|
const uint8_t OB_EXT_CMD3[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x58, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; |
||||||
|
const uint8_t OB_EXT_CMD4[16] = { 0x47, 0x4d, 0x02, 0x00, 0x03, 0x00, 0x60, 0x00, 0xed, 0x03, 0x00, 0x00 }; |
||||||
|
const uint8_t OB_EXT_CMD5[16] = { 0x47, 0x4d, 0x02, 0x00, 0x03, 0x00, 0x62, 0x00, 0xe9, 0x03, 0x00, 0x00 }; |
||||||
|
|
||||||
|
#if defined(HAVE_OBSENSOR_V4L2) |
||||||
|
#define fourCc2Int(a, b, c, d) \ |
||||||
|
((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) |
||||||
|
#elif defined(HAVE_OBSENSOR_MSMF) |
||||||
|
#define fourCc2Int(a, b, c, d) \ |
||||||
|
(((uint32_t)(a) <<24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | (uint32_t)(d)) |
||||||
|
#endif // HAVE_OBSENSOR_V4L2
|
||||||
|
|
||||||
|
const std::map<uint32_t, FrameFormat> fourccToOBFormat = { |
||||||
|
{fourCc2Int('Y', 'U', 'Y', '2'), FRAME_FORMAT_YUYV}, |
||||||
|
{fourCc2Int('M', 'J', 'P', 'G'), FRAME_FORMAT_MJPG}, |
||||||
|
{fourCc2Int('Y', '1', '6', ' '), FRAME_FORMAT_Y16}, |
||||||
|
}; |
||||||
|
|
||||||
|
StreamType parseUvcDeviceNameToStreamType(const std::string& devName) |
||||||
|
{ |
||||||
|
std::string uvcDevName = devName; |
||||||
|
for (size_t i = 0; i < uvcDevName.length(); i++) |
||||||
|
{ |
||||||
|
uvcDevName[i] = (char)tolower(uvcDevName[i]);
|
||||||
|
} |
||||||
|
if (uvcDevName.find(" depth") != std::string::npos) |
||||||
|
{ |
||||||
|
return OBSENSOR_STREAM_DEPTH; |
||||||
|
} |
||||||
|
else if (uvcDevName.find(" ir") != std::string::npos) |
||||||
|
{ |
||||||
|
return OBSENSOR_STREAM_IR; |
||||||
|
} |
||||||
|
|
||||||
|
return OBSENSOR_STREAM_COLOR; // else
|
||||||
|
} |
||||||
|
|
||||||
|
FrameFormat frameFourccToFormat(uint32_t fourcc) |
||||||
|
{ |
||||||
|
for (const auto& item : fourccToOBFormat) |
||||||
|
{ |
||||||
|
if (item.first == fourcc) |
||||||
|
{ |
||||||
|
return item.second; |
||||||
|
} |
||||||
|
} |
||||||
|
return FRAME_FORMAT_UNKNOWN; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t frameFormatToFourcc(FrameFormat fmt) |
||||||
|
{ |
||||||
|
for (const auto& item : fourccToOBFormat) |
||||||
|
{ |
||||||
|
if (item.second == fmt) |
||||||
|
{ |
||||||
|
return item.first; |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<Ptr<IStreamChannel>> getStreamChannelGroup(uint32_t groupIdx) |
||||||
|
{ |
||||||
|
std::vector<Ptr<IStreamChannel>> streamChannelGroup; |
||||||
|
|
||||||
|
#if defined(HAVE_OBSENSOR_V4L2) |
||||||
|
auto& ctx = V4L2Context::getInstance(); |
||||||
|
#elif defined(HAVE_OBSENSOR_MSMF) |
||||||
|
auto& ctx = MFContext::getInstance(); |
||||||
|
#endif // HAVE_OBSENSOR_V4L2
|
||||||
|
|
||||||
|
auto uvcDevInfoList = ctx.queryUvcDeviceInfoList(); |
||||||
|
|
||||||
|
std::map<std::string, std::vector<UvcDeviceInfo>> uvcDevInfoGroupMap; |
||||||
|
|
||||||
|
auto devInfoIter = uvcDevInfoList.begin(); |
||||||
|
while (devInfoIter != uvcDevInfoList.end()) |
||||||
|
{ |
||||||
|
if (devInfoIter->vid != OBSENSOR_CAM_VID) |
||||||
|
{ |
||||||
|
devInfoIter = uvcDevInfoList.erase(devInfoIter); // drop it
|
||||||
|
continue; |
||||||
|
} |
||||||
|
devInfoIter++; |
||||||
|
} |
||||||
|
|
||||||
|
if (!uvcDevInfoList.empty() && uvcDevInfoList.size() <= 3) |
||||||
|
{ |
||||||
|
uvcDevInfoGroupMap.insert({ "default", uvcDevInfoList }); |
||||||
|
} |
||||||
|
else { |
||||||
|
for (auto& devInfo : uvcDevInfoList) |
||||||
|
{ |
||||||
|
// group by uid
|
||||||
|
uvcDevInfoGroupMap[devInfo.uid].push_back(devInfo); // todo: group by sn
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (uvcDevInfoGroupMap.size() > groupIdx) |
||||||
|
{ |
||||||
|
auto uvcDevInfoGroupIter = uvcDevInfoGroupMap.begin(); |
||||||
|
std::advance(uvcDevInfoGroupIter, groupIdx); |
||||||
|
for (const auto& devInfo : uvcDevInfoGroupIter->second) |
||||||
|
{ |
||||||
|
streamChannelGroup.emplace_back(ctx.createStreamChannel(devInfo)); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CV_LOG_ERROR(NULL, "Camera index out of range"); |
||||||
|
} |
||||||
|
return streamChannelGroup; |
||||||
|
} |
||||||
|
|
||||||
|
DepthFrameProcessor::DepthFrameProcessor(const OBExtensionParam& param) : param_(param) |
||||||
|
{ |
||||||
|
double tempValue = 0; |
||||||
|
double rstValue = 0; |
||||||
|
lookUpTable_ = new uint16_t[4096]; |
||||||
|
memset(lookUpTable_, 0, 4096 * 2); |
||||||
|
for (uint16_t oriValue = 0; oriValue < 4096; oriValue++) |
||||||
|
{ |
||||||
|
if (oriValue == 0) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
tempValue = 200.375 - (double)oriValue / 8; |
||||||
|
rstValue = (double)param_.pd / (1 + tempValue * param_.ps / param_.bl) * 10; |
||||||
|
if ((rstValue >= 40) && (rstValue <= 10000) && rstValue < 65536) |
||||||
|
{ |
||||||
|
lookUpTable_[oriValue] = (uint16_t)rstValue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
DepthFrameProcessor::~DepthFrameProcessor() |
||||||
|
{ |
||||||
|
delete[] lookUpTable_; |
||||||
|
} |
||||||
|
|
||||||
|
void DepthFrameProcessor::process(Frame* frame) |
||||||
|
{ |
||||||
|
uint16_t* data = (uint16_t*)frame->data; |
||||||
|
for (uint32_t i = 0; i < frame->dataSize / 2; i++) |
||||||
|
{ |
||||||
|
data[i] = lookUpTable_[data[i] & 0x0fff]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
IUvcStreamChannel::IUvcStreamChannel(const UvcDeviceInfo& devInfo) : |
||||||
|
devInfo_(devInfo), |
||||||
|
streamType_(parseUvcDeviceNameToStreamType(devInfo_.name)) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
StreamType IUvcStreamChannel::streamType() const { |
||||||
|
return streamType_; |
||||||
|
} |
||||||
|
|
||||||
|
bool IUvcStreamChannel::setProperty(int propId, const uint8_t* /*data*/, uint32_t /*dataSize*/) |
||||||
|
{ |
||||||
|
uint8_t* rcvData; |
||||||
|
uint32_t rcvLen; |
||||||
|
bool rst = true; |
||||||
|
switch (propId) |
||||||
|
{ |
||||||
|
case DEPTH_TO_COLOR_ALIGN: |
||||||
|
// todo: value filling
|
||||||
|
rst &= setXu(2, OB_EXT_CMD0, sizeof(OB_EXT_CMD0)); |
||||||
|
rst &= getXu(2, &rcvData, &rcvLen); |
||||||
|
rst &= setXu(2, OB_EXT_CMD1, sizeof(OB_EXT_CMD1)); |
||||||
|
rst &= getXu(2, &rcvData, &rcvLen); |
||||||
|
rst &= setXu(2, OB_EXT_CMD2, sizeof(OB_EXT_CMD2)); |
||||||
|
rst &= getXu(2, &rcvData, &rcvLen); |
||||||
|
rst &= setXu(2, OB_EXT_CMD3, sizeof(OB_EXT_CMD3)); |
||||||
|
rst &= getXu(2, &rcvData, &rcvLen); |
||||||
|
break; |
||||||
|
default: |
||||||
|
rst = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
return rst; |
||||||
|
} |
||||||
|
|
||||||
|
bool IUvcStreamChannel::getProperty(int propId, uint8_t* recvData, uint32_t* recvDataSize) |
||||||
|
{ |
||||||
|
bool rst = true; |
||||||
|
uint8_t* rcvData; |
||||||
|
uint32_t rcvLen; |
||||||
|
switch (propId) |
||||||
|
{ |
||||||
|
case CAMERA_PARAM: |
||||||
|
rst &= setXu(2, OB_EXT_CMD5, sizeof(OB_EXT_CMD5)); |
||||||
|
rst &= getXu(2, &rcvData, &rcvLen); |
||||||
|
if (rst && OB_EXT_CMD5[6] == rcvData[6] && rcvData[8] == 0 && rcvData[9] == 0) |
||||||
|
{ |
||||||
|
memcpy(recvData, rcvData + 10, rcvLen - 10); |
||||||
|
*recvDataSize = rcvLen - 10; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
rst = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return rst; |
||||||
|
} |
||||||
|
|
||||||
|
bool IUvcStreamChannel::initDepthFrameProcessor() |
||||||
|
{ |
||||||
|
if (streamType_ == OBSENSOR_STREAM_DEPTH && setXu(2, OB_EXT_CMD4, sizeof(OB_EXT_CMD4))) |
||||||
|
{ |
||||||
|
uint8_t* rcvData; |
||||||
|
uint32_t rcvLen; |
||||||
|
if (getXu(1, &rcvData, &rcvLen) && OB_EXT_CMD4[6] == rcvData[6] && rcvData[8] == 0 && rcvData[9] == 0) |
||||||
|
{ |
||||||
|
depthFrameProcessor_ = makePtr<DepthFrameProcessor>(*(OBExtensionParam*)(rcvData + 10)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR_V4L2 || HAVE_OBSENSOR_MSMF
|
@ -0,0 +1,96 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OPENCV_VIDEOIO_OBSENSOR_UVC_STREAM_CHANNEL_HPP |
||||||
|
#define OPENCV_VIDEOIO_OBSENSOR_UVC_STREAM_CHANNEL_HPP |
||||||
|
#include "obsensor_stream_channel_interface.hpp" |
||||||
|
|
||||||
|
#ifdef HAVE_OBSENSOR |
||||||
|
namespace cv { |
||||||
|
namespace obsensor { |
||||||
|
|
||||||
|
#define OBSENSOR_CAM_VID 0x2bc5 // usb vid
|
||||||
|
#define XU_MAX_DATA_LENGTH 1024 |
||||||
|
#define XU_UNIT_ID 4 |
||||||
|
|
||||||
|
struct UvcDeviceInfo |
||||||
|
{ |
||||||
|
std::string id = ""; // uvc sub-device id
|
||||||
|
std::string name = ""; |
||||||
|
std::string uid = ""; // parent usb device id
|
||||||
|
uint16_t vid = 0; |
||||||
|
uint16_t pid = 0; |
||||||
|
uint16_t mi = 0; // uvc interface index
|
||||||
|
}; |
||||||
|
|
||||||
|
enum StreamState |
||||||
|
{ |
||||||
|
STREAM_STOPED = 0, // stoped or ready
|
||||||
|
STREAM_STARTING = 1, |
||||||
|
STREAM_STARTED = 2, |
||||||
|
STREAM_STOPPING = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
StreamType parseUvcDeviceNameToStreamType(const std::string& devName); |
||||||
|
FrameFormat frameFourccToFormat(uint32_t fourcc); |
||||||
|
uint32_t frameFormatToFourcc(FrameFormat); |
||||||
|
|
||||||
|
struct OBExtensionParam { |
||||||
|
float bl; |
||||||
|
float bl2; |
||||||
|
float pd; |
||||||
|
float ps; |
||||||
|
}; |
||||||
|
|
||||||
|
class DepthFrameProcessor { |
||||||
|
public: |
||||||
|
DepthFrameProcessor(const OBExtensionParam& parma); |
||||||
|
~DepthFrameProcessor() noexcept; |
||||||
|
void process(Frame* frame); |
||||||
|
|
||||||
|
private: |
||||||
|
const OBExtensionParam param_; |
||||||
|
uint16_t* lookUpTable_; |
||||||
|
}; |
||||||
|
|
||||||
|
class IUvcStreamChannel : public IStreamChannel { |
||||||
|
public: |
||||||
|
IUvcStreamChannel(const UvcDeviceInfo& devInfo); |
||||||
|
virtual ~IUvcStreamChannel() noexcept {} |
||||||
|
|
||||||
|
virtual bool setProperty(int propId, const uint8_t* data, uint32_t dataSize) override; |
||||||
|
virtual bool getProperty(int propId, uint8_t* recvData, uint32_t* recvDataSize) override; |
||||||
|
virtual StreamType streamType() const override; |
||||||
|
|
||||||
|
protected: |
||||||
|
virtual bool setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) = 0; |
||||||
|
virtual bool getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) = 0; |
||||||
|
|
||||||
|
bool initDepthFrameProcessor(); |
||||||
|
|
||||||
|
protected: |
||||||
|
const UvcDeviceInfo devInfo_; |
||||||
|
StreamType streamType_; |
||||||
|
Ptr<DepthFrameProcessor> depthFrameProcessor_; |
||||||
|
}; |
||||||
|
}} // namespace cv::obsensor::
|
||||||
|
#endif // HAVE_OBSENSOR
|
||||||
|
#endif // OPENCV_VIDEOIO_OBSENSOR_UVC_STREAM_CHANNEL_HPP
|
@ -0,0 +1,150 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "precomp.hpp" |
||||||
|
|
||||||
|
#include "cap_obsensor_capture.hpp" |
||||||
|
#include "cap_obsensor/obsensor_stream_channel_interface.hpp" |
||||||
|
#ifdef HAVE_OBSENSOR |
||||||
|
namespace cv { |
||||||
|
Ptr<IVideoCapture> create_obsensor_capture(int index) |
||||||
|
{ |
||||||
|
return makePtr<VideoCapture_obsensor>(index); |
||||||
|
} |
||||||
|
|
||||||
|
VideoCapture_obsensor::VideoCapture_obsensor(int index) : isOpened_(false) |
||||||
|
{ |
||||||
|
static const obsensor::StreamProfile colorProfile = { 640, 480, 30, obsensor::FRAME_FORMAT_MJPG }; |
||||||
|
static const obsensor::StreamProfile depthProfile = {640, 480, 30, obsensor::FRAME_FORMAT_Y16}; |
||||||
|
|
||||||
|
streamChannelGroup_ = obsensor::getStreamChannelGroup(index); |
||||||
|
if (!streamChannelGroup_.empty()) |
||||||
|
{ |
||||||
|
for (auto& channel : streamChannelGroup_) |
||||||
|
{ |
||||||
|
auto streamType = channel->streamType(); |
||||||
|
switch (streamType) |
||||||
|
{ |
||||||
|
case obsensor::OBSENSOR_STREAM_COLOR: |
||||||
|
channel->start(colorProfile, [&](obsensor::Frame* frame) { |
||||||
|
std::unique_lock<std::mutex> lk(frameMutex_); |
||||||
|
colorFrame_ = Mat(1, frame->dataSize, CV_8UC1, frame->data).clone(); |
||||||
|
}); |
||||||
|
break; |
||||||
|
case obsensor::OBSENSOR_STREAM_DEPTH: |
||||||
|
{ |
||||||
|
uint8_t data = 1; |
||||||
|
channel->setProperty(obsensor::DEPTH_TO_COLOR_ALIGN, &data, 1); |
||||||
|
channel->start(depthProfile, [&](obsensor::Frame* frame) { |
||||||
|
std::unique_lock<std::mutex> lk(frameMutex_); |
||||||
|
depthFrame_ = Mat(frame->height, frame->width, CV_16UC1, frame->data, frame->width * 2).clone(); |
||||||
|
}); |
||||||
|
|
||||||
|
uint32_t len; |
||||||
|
memset(&camParam_, 0, sizeof(camParam_)); |
||||||
|
channel->getProperty(obsensor::CAMERA_PARAM, (uint8_t*)&camParam_, &len); |
||||||
|
camParamScale_ = (int)(camParam_.p1[2] * 2 / 640 + 0.5); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
isOpened_ = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool VideoCapture_obsensor::grabFrame() |
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> lk(frameMutex_); |
||||||
|
|
||||||
|
grabbedDepthFrame_ = depthFrame_; |
||||||
|
grabbedColorFrame_ = colorFrame_; |
||||||
|
|
||||||
|
depthFrame_.release(); |
||||||
|
colorFrame_.release(); |
||||||
|
|
||||||
|
return !grabbedDepthFrame_.empty() || !grabbedColorFrame_.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
bool VideoCapture_obsensor::retrieveFrame(int outputType, OutputArray frame) |
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> lk(frameMutex_); |
||||||
|
switch (outputType) |
||||||
|
{ |
||||||
|
case CAP_OBSENSOR_DEPTH_MAP: |
||||||
|
if (!grabbedDepthFrame_.empty()) |
||||||
|
{ |
||||||
|
grabbedDepthFrame_.copyTo(frame); |
||||||
|
grabbedDepthFrame_.release(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
break; |
||||||
|
case CAP_OBSENSOR_BGR_IMAGE: |
||||||
|
if (!grabbedColorFrame_.empty()) |
||||||
|
{ |
||||||
|
auto mat = imdecode(grabbedColorFrame_, IMREAD_COLOR); |
||||||
|
grabbedColorFrame_.release(); |
||||||
|
|
||||||
|
if (!mat.empty()) |
||||||
|
{ |
||||||
|
mat.copyTo(frame); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
double VideoCapture_obsensor::getProperty(int propIdx) const { |
||||||
|
double rst = 0.0; |
||||||
|
propIdx = propIdx & (~CAP_OBSENSOR_GENERATORS_MASK); |
||||||
|
// int gen = propIdx & CAP_OBSENSOR_GENERATORS_MASK;
|
||||||
|
switch (propIdx) |
||||||
|
{ |
||||||
|
case CAP_PROP_OBSENSOR_INTRINSIC_FX: |
||||||
|
rst = camParam_.p1[0] / camParamScale_; |
||||||
|
break; |
||||||
|
case CAP_PROP_OBSENSOR_INTRINSIC_FY: |
||||||
|
rst = camParam_.p1[1] / camParamScale_; |
||||||
|
break; |
||||||
|
case CAP_PROP_OBSENSOR_INTRINSIC_CX: |
||||||
|
rst = camParam_.p1[2] / camParamScale_; |
||||||
|
break; |
||||||
|
case CAP_PROP_OBSENSOR_INTRINSIC_CY: |
||||||
|
rst = camParam_.p1[3] / camParamScale_; |
||||||
|
break; |
||||||
|
} |
||||||
|
return rst; |
||||||
|
} |
||||||
|
|
||||||
|
bool VideoCapture_obsensor::setProperty(int propIdx, double /*propVal*/) |
||||||
|
{ |
||||||
|
CV_LOG_WARNING(NULL, "Unsupported or read only property, id=" << propIdx); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace cv::
|
||||||
|
#endif // HAVE_OBSENSOR
|
@ -0,0 +1,66 @@ |
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright(C) 2022 by ORBBEC Technology., Inc. |
||||||
|
* Authors: |
||||||
|
* Huang Zhenchang <yufeng@orbbec.com> |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OPENCV_VIDEOIO_CAP_OBSENSOR_CAPTURE_HPP |
||||||
|
#define OPENCV_VIDEOIO_CAP_OBSENSOR_CAPTURE_HPP |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <mutex> |
||||||
|
|
||||||
|
#include "cap_obsensor/obsensor_stream_channel_interface.hpp" |
||||||
|
|
||||||
|
#ifdef HAVE_OBSENSOR |
||||||
|
namespace cv { |
||||||
|
class VideoCapture_obsensor : public IVideoCapture |
||||||
|
{ |
||||||
|
public: |
||||||
|
VideoCapture_obsensor(int index); |
||||||
|
virtual ~VideoCapture_obsensor() {} |
||||||
|
|
||||||
|
virtual double getProperty(int propIdx) const CV_OVERRIDE; |
||||||
|
virtual bool setProperty(int propIdx, double propVal) CV_OVERRIDE; |
||||||
|
virtual bool grabFrame() CV_OVERRIDE; |
||||||
|
virtual bool retrieveFrame(int outputType, OutputArray frame) CV_OVERRIDE; |
||||||
|
virtual int getCaptureDomain() CV_OVERRIDE { |
||||||
|
return CAP_OBSENSOR; |
||||||
|
} |
||||||
|
virtual bool isOpened() const CV_OVERRIDE { |
||||||
|
return isOpened_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
bool isOpened_; |
||||||
|
std::vector<Ptr<obsensor::IStreamChannel>> streamChannelGroup_; |
||||||
|
|
||||||
|
std::mutex frameMutex_; |
||||||
|
|
||||||
|
Mat depthFrame_; |
||||||
|
Mat colorFrame_; |
||||||
|
|
||||||
|
Mat grabbedDepthFrame_; |
||||||
|
Mat grabbedColorFrame_; |
||||||
|
|
||||||
|
obsensor::CameraParam camParam_; |
||||||
|
int camParamScale_; |
||||||
|
}; |
||||||
|
} // namespace cv::
|
||||||
|
#endif // HAVE_OBSENSOR
|
||||||
|
#endif // OPENCV_VIDEOIO_CAP_OBSENSOR_CAPTURE_HPP
|
@ -0,0 +1,74 @@ |
|||||||
|
#include <opencv2/videoio.hpp> |
||||||
|
#include <opencv2/highgui.hpp> |
||||||
|
#include <opencv2/imgproc.hpp> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
using namespace cv; |
||||||
|
int main() |
||||||
|
{ |
||||||
|
VideoCapture obsensorCapture(0, CAP_OBSENSOR); |
||||||
|
if(!obsensorCapture.isOpened()){ |
||||||
|
std::cerr << "Failed to open obsensor capture! Index out of range or no response from device"; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
double fx = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_FX); |
||||||
|
double fy = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_FY); |
||||||
|
double cx = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_CX); |
||||||
|
double cy = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_CY); |
||||||
|
std::cout << "obsensor camera intrinsic params: fx=" << fx << ", fy=" << fy << ", cx=" << cx << ", cy=" << cy << std::endl; |
||||||
|
|
||||||
|
Mat image; |
||||||
|
Mat depthMap; |
||||||
|
Mat adjDepthMap; |
||||||
|
while (true) |
||||||
|
{ |
||||||
|
// Grab depth map like this:
|
||||||
|
// obsensorCapture >> depthMap;
|
||||||
|
|
||||||
|
// Another way to grab depth map (and bgr image).
|
||||||
|
if (obsensorCapture.grab()) |
||||||
|
{ |
||||||
|
if (obsensorCapture.retrieve(image, CAP_OBSENSOR_BGR_IMAGE)) |
||||||
|
{ |
||||||
|
imshow("RGB", image); |
||||||
|
} |
||||||
|
|
||||||
|
if (obsensorCapture.retrieve(depthMap, CAP_OBSENSOR_DEPTH_MAP)) |
||||||
|
{ |
||||||
|
normalize(depthMap, adjDepthMap, 0, 255, NORM_MINMAX, CV_8UC1); |
||||||
|
applyColorMap(adjDepthMap, adjDepthMap, COLORMAP_JET); |
||||||
|
imshow("DEPTH", adjDepthMap); |
||||||
|
} |
||||||
|
|
||||||
|
// depth map overlay on bgr image
|
||||||
|
static const float alpha = 0.6f; |
||||||
|
if (!image.empty() && !depthMap.empty()) |
||||||
|
{ |
||||||
|
normalize(depthMap, adjDepthMap, 0, 255, NORM_MINMAX, CV_8UC1); |
||||||
|
cv::resize(adjDepthMap, adjDepthMap, cv::Size(image.cols, image.rows)); |
||||||
|
for (int i = 0; i < image.rows; i++) |
||||||
|
{ |
||||||
|
for (int j = 0; j < image.cols; j++) |
||||||
|
{ |
||||||
|
cv::Vec3b& outRgb = image.at<cv::Vec3b>(i, j); |
||||||
|
uint8_t depthValue = 255 - adjDepthMap.at<uint8_t>(i, j); |
||||||
|
if (depthValue != 0 && depthValue != 255) |
||||||
|
{ |
||||||
|
outRgb[0] = (uint8_t)(outRgb[0] * (1.0f - alpha) + depthValue * alpha); |
||||||
|
outRgb[1] = (uint8_t)(outRgb[1] * (1.0f - alpha) + depthValue * alpha); |
||||||
|
outRgb[2] = (uint8_t)(outRgb[2] * (1.0f - alpha) + depthValue * alpha); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
imshow("DepthToColor", image); |
||||||
|
} |
||||||
|
image.release(); |
||||||
|
depthMap.release(); |
||||||
|
} |
||||||
|
|
||||||
|
if (pollKey() >= 0) |
||||||
|
break; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue