Commit 4418ee6c authored by Maxim Kostin's avatar Maxim Kostin

Refactored internal helpers. Simplified structure. Updated comments. Updates #41

Signed-off-by: 's avatarMaxim Kostin <v-maxkos@microsoft.com>
parent 3136010e
...@@ -54,8 +54,11 @@ CV_EXPORTS void winrt_startMessageLoop(void callback(Args...), Args... args); ...@@ -54,8 +54,11 @@ CV_EXPORTS void winrt_startMessageLoop(void callback(Args...), Args... args);
/** @brief /** @brief
@note @note
Sets the reporter method for the HighguiAssist singleton. Starts the main OpenCV as Starts (1) frame-grabbing loop and (2) message loop
an async thread in WinRT. See VideoCapture for the example of callback implementation. 1. Function passed as an argument must implement common OCV reading frames
pattern (see cv::VideoCapture documentation) AND call cv::winrt_imgshow().
2. Message processing loop required to overcome WinRT container and type
conversion restrictions. OCV provides default implementation
Here is how the class can be used: Here is how the class can be used:
@code @code
void cvMain() void cvMain()
......
...@@ -697,7 +697,7 @@ VideoCapture& VideoCapture::operator >> (Mat& image) ...@@ -697,7 +697,7 @@ VideoCapture& VideoCapture::operator >> (Mat& image)
bridge.bIsFrameNew = false; bridge.bIsFrameNew = false;
// needed here because setting Mat 'image' is not allowed by OutputArray in read() // needed here because setting Mat 'image' is not allowed by OutputArray in read()
Mat m(bridge.height, bridge.width, CV_8UC3, p); Mat m(bridge.getHeight(), bridge.getWidth(), CV_8UC3, p);
image = m; image = m;
} }
} }
......
...@@ -227,7 +227,6 @@ HRESULT MediaStreamSink::IsMediaTypeSupported(__in IMFMediaType *mediaType, __de ...@@ -227,7 +227,6 @@ HRESULT MediaStreamSink::IsMediaTypeSupported(__in IMFMediaType *mediaType, __de
HRESULT hr = ExceptionBoundary([this, mediaType, closestMediaType, &supported]() HRESULT hr = ExceptionBoundary([this, mediaType, closestMediaType, &supported]()
{ {
auto lock = _lock.LockExclusive(); auto lock = _lock.LockExclusive();
HRESULT hr = S_OK;
if (closestMediaType != nullptr) if (closestMediaType != nullptr)
{ {
...@@ -281,7 +280,6 @@ HRESULT MediaStreamSink::SetCurrentMediaType(__in IMFMediaType *mediaType) ...@@ -281,7 +280,6 @@ HRESULT MediaStreamSink::SetCurrentMediaType(__in IMFMediaType *mediaType)
return ExceptionBoundary([this, mediaType]() return ExceptionBoundary([this, mediaType]()
{ {
auto lock = _lock.LockExclusive(); auto lock = _lock.LockExclusive();
HRESULT hr = S_OK;
CHKNULL(mediaType); CHKNULL(mediaType);
......
...@@ -45,7 +45,7 @@ using namespace ::std; ...@@ -45,7 +45,7 @@ using namespace ::std;
/***************************** VideoioBridge class ******************************/ /***************************** VideoioBridge class ******************************/
// non-blocking // non-blocking
void VideoioBridge::requestForUIthreadAsync(int action, int widthp, int heightp) void VideoioBridge::requestForUIthreadAsync(int action)
{ {
reporter.report(action); reporter.report(action);
} }
...@@ -80,10 +80,79 @@ void VideoioBridge::allocateOutputBuffers() ...@@ -80,10 +80,79 @@ void VideoioBridge::allocateOutputBuffers()
backOutputBuffer = ref new WriteableBitmap(width, height); backOutputBuffer = ref new WriteableBitmap(width, height);
} }
// performed on UI thread
void VideoioBridge::allocateBuffers(int width, int height)
{
// allocate input Mats (bgra8 = CV_8UC4, RGB24 = CV_8UC3)
frontInputMat.create(height, width, CV_8UC3);
backInputMat.create(height, width, CV_8UC3);
frontInputPtr = frontInputMat.ptr(0);
backInputPtr = backInputMat.ptr(0);
allocateOutputBuffers();
}
// performed on UI thread
bool VideoioBridge::openCamera()
{
// buffers must alloc'd on UI thread
allocateBuffers(width, height);
// nb. video capture device init must be done on UI thread;
if (!Video::getInstance().isStarted())
{
Video::getInstance().initGrabber(deviceIndex, width, height);
return true;
}
return false;
}
// nb on UI thread
void VideoioBridge::updateFrameContainer()
{
// copy output Mat to WBM
Video::getInstance().CopyOutput();
// set XAML image element with image WBM
cvImage->Source = backOutputBuffer;
}
void VideoioBridge::imshow() void VideoioBridge::imshow()
{ {
VideoioBridge::getInstance().swapOutputBuffers(); swapOutputBuffers();
VideoioBridge::getInstance().requestForUIthreadAsync(cv::UPDATE_IMAGE_ELEMENT); requestForUIthreadAsync(cv::UPDATE_IMAGE_ELEMENT);
}
int VideoioBridge::getDeviceIndex()
{
return deviceIndex;
}
void VideoioBridge::setDeviceIndex(int index)
{
deviceIndex = index;
}
int VideoioBridge::getWidth()
{
return width;
}
int VideoioBridge::getHeight()
{
return height;
}
void VideoioBridge::setWidth(int _width)
{
width = _width;
}
void VideoioBridge::setHeight(int _height)
{
height = _height;
} }
// end // end
\ No newline at end of file
...@@ -53,16 +53,24 @@ public: ...@@ -53,16 +53,24 @@ public:
void setReporter(Concurrency::progress_reporter<int> pr) { reporter = pr; } void setReporter(Concurrency::progress_reporter<int> pr) { reporter = pr; }
// to be called from cvMain via cap_winrt on bg thread - non-blocking (async) // to be called from cvMain via cap_winrt on bg thread - non-blocking (async)
void requestForUIthreadAsync( int action, int width=0, int height=0 ); void requestForUIthreadAsync(int action);
// TODO: modify in window.cpp: void cv::imshow( const String& winname, InputArray _img ) // TODO: modify in window.cpp: void cv::imshow( const String& winname, InputArray _img )
void imshow(/*cv::InputArray matToShow*/); // shows Mat in the cvImage element void imshow(/*cv::InputArray matToShow*/); // shows Mat in the cvImage element
void swapInputBuffers(); void swapInputBuffers();
void allocateOutputBuffers(); void allocateOutputBuffers();
void swapOutputBuffers(); void swapOutputBuffers();
void updateFrameContainer();
bool openCamera();
void allocateBuffers(int width, int height);
int getDeviceIndex();
void setDeviceIndex(int index);
int getWidth();
void setWidth(int width);
int getHeight();
void setHeight(int height);
int deviceIndex, width, height;
std::atomic<bool> bIsFrameNew; std::atomic<bool> bIsFrameNew;
std::mutex inputBufferMutex; // input is double buffered std::mutex inputBufferMutex; // input is double buffered
unsigned char * frontInputPtr; // OpenCV reads this unsigned char * frontInputPtr; // OpenCV reads this
...@@ -93,4 +101,17 @@ private: ...@@ -93,4 +101,17 @@ private:
std::atomic<bool> deviceReady; std::atomic<bool> deviceReady;
Concurrency::progress_reporter<int> reporter; Concurrency::progress_reporter<int> reporter;
// Mats are wrapped with singleton class, we do not support more than one
// capture device simultaneously with the design at this time
//
// nb. inputBufferMutex was not able to guarantee that OpenCV Mats were
// ready to accept data in the UI thread (memory access exceptions were thrown
// even though buffer address was good).
// Therefore allocation of Mats is also done on the UI thread before the video
// device is initialized.
cv::Mat frontInputMat;
cv::Mat backInputMat;
int deviceIndex, width, height;
}; };
\ No newline at end of file
...@@ -43,23 +43,9 @@ using namespace Microsoft::WRL; ...@@ -43,23 +43,9 @@ using namespace Microsoft::WRL;
using namespace ::std; using namespace ::std;
// nb. VideoCapture_WinRT is not a singleton, so the Mats are made file statics
// we do not support more than one capture device simultaneously with the
// design at this time
// nb. inputBufferMutex was not able to guarantee that OpenCV Mats were
// ready to accept data in the UI thread (memory access exceptions were thrown
// even though buffer address was good).
// Therefore allocation of Mats is also done on the UI thread before the video
// device is initialized.
static cv::Mat frontInputMat;
static cv::Mat backInputMat;
namespace cv { namespace cv {
/***************************** exported control functions ******************************/ /******************************* exported API functions **************************************/
template <typename ...Args> template <typename ...Args>
void winrt_startMessageLoop(std::function<void(Args...)>&& callback, Args... args) void winrt_startMessageLoop(std::function<void(Args...)>&& callback, Args... args)
...@@ -80,13 +66,13 @@ namespace cv { ...@@ -80,13 +66,13 @@ namespace cv {
switch (action) switch (action)
{ {
case OPEN_CAMERA: case OPEN_CAMERA:
winrt_openCamera(); VideoioBridge::getInstance().openCamera();
break; break;
case CLOSE_CAMERA: case CLOSE_CAMERA:
winrt_closeGrabber(); Video::getInstance().closeGrabber();
break; break;
case UPDATE_IMAGE_ELEMENT: case UPDATE_IMAGE_ELEMENT:
winrt_updateFrameContainer(); VideoioBridge::getInstance().updateFrameContainer();
break; break;
} }
}); });
...@@ -98,7 +84,8 @@ namespace cv { ...@@ -98,7 +84,8 @@ namespace cv {
winrt_startMessageLoop(std::function<void(Args...)>(callback), args...); winrt_startMessageLoop(std::function<void(Args...)>(callback), args...);
} }
void winrt_onVisibilityChanged(bool visible) { void winrt_onVisibilityChanged(bool visible)
{
if (visible) if (visible)
{ {
VideoioBridge& bridge = VideoioBridge::getInstance(); VideoioBridge& bridge = VideoioBridge::getInstance();
...@@ -108,99 +95,34 @@ namespace cv { ...@@ -108,99 +95,34 @@ namespace cv {
{ {
if (Video::getInstance().isStarted()) return; if (Video::getInstance().isStarted()) return;
int device = bridge.deviceIndex; int device = bridge.getDeviceIndex();
int width = bridge.width; int width = bridge.getWidth();
int height = bridge.height; int height = bridge.getHeight();
winrt_initGrabber(device, width, height); Video::getInstance().initGrabber(device, width, height);
} }
} else } else
{ {
//grabberStarted = false; //grabberStarted = false;
winrt_closeGrabber(); Video::getInstance().closeGrabber();
}
}
void winrt_imshow() {
VideoioBridge::getInstance().imshow();
}
void winrt_setFrameContainer(::Windows::UI::Xaml::Controls::Image^ image) {
VideoioBridge::getInstance().cvImage = image;
} }
/********************************* Internal helpers ************************************/
void winrt_updateFrameContainer()
{
// copy output Mat to WBM
winrt_copyOutput();
// set XAML image element with image WBM
VideoioBridge::getInstance().cvImage->Source = VideoioBridge::getInstance().backOutputBuffer;
} }
// performed on UI thread void winrt_imshow()
bool winrt_openCamera()
{
VideoioBridge& bridge = VideoioBridge::getInstance();
int device = bridge.deviceIndex;
int width = bridge.width;
int height = bridge.height;
// buffers must alloc'd on UI thread
winrt_allocateBuffers(width, height);
// nb. video capture device init must be done on UI thread;
if (!Video::getInstance().isStarted())
{ {
winrt_initGrabber(device, width, height); VideoioBridge::getInstance().imshow();
return true;
}
return false;
} }
// performed on UI thread void winrt_setFrameContainer(::Windows::UI::Xaml::Controls::Image^ image)
void winrt_allocateBuffers(int width, int height)
{ {
VideoioBridge& bridge = VideoioBridge::getInstance(); VideoioBridge::getInstance().cvImage = image;
// allocate input Mats (bgra8 = CV_8UC4, RGB24 = CV_8UC3)
frontInputMat.create(height, width, CV_8UC3);
backInputMat.create(height, width, CV_8UC3);
bridge.frontInputPtr = frontInputMat.ptr(0);
bridge.backInputPtr = backInputMat.ptr(0);
bridge.allocateOutputBuffers();
}
// non-blocking
bool winrt_initGrabber(int device, int w, int h) {
// nb. Video class is not exported outside of this DLL
// due to complexities in the CaptureFrameGrabber ref class
// as written in the header not mixing well with pure C++ classes
return Video::getInstance().initGrabber(device, w, h);
}
void winrt_closeGrabber() {
Video::getInstance().closeGrabber();
}
// nb on UI thread
void winrt_copyOutput() {
Video::getInstance().CopyOutput();
} }
/********************************* VideoCapture_WinRT class ****************************/ /********************************* VideoCapture_WinRT class ****************************/
VideoCapture_WinRT::VideoCapture_WinRT(int device) : started(false) VideoCapture_WinRT::VideoCapture_WinRT(int device) : started(false)
{ {
VideoioBridge::getInstance().deviceIndex = device; VideoioBridge::getInstance().setDeviceIndex(device);
} }
bool VideoCapture_WinRT::isOpened() const bool VideoCapture_WinRT::isOpened() const
...@@ -240,14 +162,13 @@ namespace cv { ...@@ -240,14 +162,13 @@ namespace cv {
if (width == 0) width = 640; if (width == 0) width = 640;
if (height == 0) height = 480; if (height == 0) height = 480;
VideoioBridge::getInstance().width = width; VideoioBridge::getInstance().setWidth(width);
VideoioBridge::getInstance().height = height; VideoioBridge::getInstance().setHeight(height);
// nb. Mats will be alloc'd on UI thread // nb. Mats will be alloc'd on UI thread
// request device init on UI thread - this does not block, and is async // request device init on UI thread - this does not block, and is async
VideoioBridge::getInstance().requestForUIthreadAsync(OPEN_CAMERA, VideoioBridge::getInstance().requestForUIthreadAsync(OPEN_CAMERA);
outArray.size().width, outArray.size().height);
started = true; started = true;
return false; return false;
......
...@@ -42,17 +42,6 @@ ...@@ -42,17 +42,6 @@
namespace cv { namespace cv {
/******************* Internal helpers **************************************/
void winrt_updateFrameContainer();
bool winrt_openCamera();
bool winrt_initGrabber(int device, int w, int h);
void winrt_closeGrabber();
void winrt_copyOutput();
void winrt_allocateBuffers(int width, int height);
/******************* VideoCapture_WinRT class ******************************/
class VideoCapture_WinRT : public IVideoCapture class VideoCapture_WinRT : public IVideoCapture
{ {
public: public:
......
...@@ -78,7 +78,7 @@ void Video::closeGrabber() { ...@@ -78,7 +78,7 @@ void Video::closeGrabber() {
bGrabberInitInProgress = false; bGrabberInitInProgress = false;
} }
// non-blocking
bool Video::initGrabber(int device, int w, int h) { bool Video::initGrabber(int device, int w, int h) {
// already started? // already started?
if (bGrabberInited || bGrabberInitInProgress) return false; if (bGrabberInited || bGrabberInitInProgress) return false;
...@@ -124,7 +124,7 @@ bool Video::initGrabber(int device, int w, int h) { ...@@ -124,7 +124,7 @@ bool Video::initGrabber(int device, int w, int h) {
// for 24 bpp // for 24 bpp
props->Subtype = MediaEncodingSubtypes::Rgb24; bytesPerPixel = 3; props->Subtype = MediaEncodingSubtypes::Rgb24; bytesPerPixel = 3;
// format used by XAML & WBM (for testing) // XAML & WBM use BGRA8, so it would look like
// props->Subtype = MediaEncodingSubtypes::Bgra8; bytesPerPixel = 4; // props->Subtype = MediaEncodingSubtypes::Bgra8; bytesPerPixel = 4;
props->Width = width; props->Width = width;
...@@ -282,22 +282,20 @@ bool Video::listDevicesTask() { ...@@ -282,22 +282,20 @@ bool Video::listDevicesTask() {
auto settings = ref new MediaCaptureInitializationSettings(); auto settings = ref new MediaCaptureInitializationSettings();
//vector <int> devices;
create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture)) create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture))
.then([this, &ready](task<DeviceInformationCollection^> findTask) .then([this, &ready](task<DeviceInformationCollection^> findTask)
{ {
m_devices = findTask.get(); m_devices = findTask.get();
for (size_t i = 0; i < m_devices->Size; i++) // TODO: collect device data
{ // for (size_t i = 0; i < m_devices->Size; i++)
// ofVideoDevice deviceInfo; // {
auto d = m_devices->GetAt(i); // .. deviceInfo;
//deviceInfo.bAvailable = true; // auto d = m_devices->GetAt(i);
//deviceInfo.deviceName = PlatformStringToString(d->Name); // deviceInfo.bAvailable = true;
//deviceInfo.hardwareName = deviceInfo.deviceName; // deviceInfo.deviceName = PlatformStringToString(d->Name);
// devices.push_back(deviceInfo); // deviceInfo.hardwareName = deviceInfo.deviceName;
} // }
ready = true; ready = true;
}); });
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
class Video { class Video {
public: public:
// non-blocking
bool initGrabber(int device, int w, int h); bool initGrabber(int device, int w, int h);
void closeGrabber(); void closeGrabber();
bool isStarted(); bool isStarted();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment