// MotionSensor.cpp : Implementation of CMotionSensor #include "stdafx.h" #include "ChannelObjects.h" #include "MotionSensor.h" //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // CMotionSensor //\_ STDMETHODIMP CMotionSensor::FinalConstruct () { HRESULT hr; _ComVerifyRS( hr = AddConnector( CLSID_ChannelInputConnector, GUID_VideoDataType, alignLeft ) ); _VerifyRS( m_oVideoInput = GetConnector( hr ) ); _ComVerifyRS( hr = AddConnector( CLSID_ChannelOutputConnector, GUID_BooleanDataType, alignTop ) ); _VerifyRS( m_oSensorOutput = GetConnector( hr ) ); _ComVerifyRS( hr = AddConnector( CLSID_ChannelOutputConnector, GUID_VideoDataType, alignRight ) ); _VerifyRS( m_oVideoOutput = GetConnector( hr ) ); _ComVerifyRS( DoAdvise( m_oVideoInput, IID__IInputConnectorEvents ) ) _ComVerifyRS( DoAdvise( m_oSensorOutput, IID__IOutputConnectorEvents ) ); _ComVerifyRS( DoAdvise( m_oVideoOutput, IID__IOutputConnectorEvents ) ); _ComVerifyRS( DoAdvise( m_oVideoInput, IID__IConnectorEvents ) ) _ComVerifyRS( DoAdvise( m_oSensorOutput, IID__IConnectorEvents ) ); _ComVerifyRS( DoAdvise( m_oVideoOutput, IID__IConnectorEvents ) ); // Creates event handles _VerifyRS( m_hStopProcess = CreateEvent ( NULL, TRUE, FALSE, NULL ) ); _VerifyRS( m_hProcessImage = CreateEvent ( NULL, TRUE, FALSE, NULL ) ); _VerifyRS( m_hTimerEvent = CreateEvent ( NULL, TRUE, FALSE, NULL ) ); _VerifyRS( m_hVideoOutReady = CreateEvent ( NULL, TRUE, FALSE, NULL ) ); return S_OK; } STDMETHODIMP CMotionSensor::FinalRelease () { // Close event handles if ( m_hStopProcess ) ::CloseHandle( m_hStopProcess ), m_hStopProcess = NULL; if ( m_hProcessImage ) ::CloseHandle( m_hProcessImage ), m_hProcessImage = NULL; if ( m_hVideoOutReady ) ::CloseHandle( m_hVideoOutReady ), m_hProcessImage = NULL; if (m_nTimerID) timeKillEvent(m_nTimerID); if ( m_hTimerEvent ) ::CloseHandle( m_hTimerEvent ), m_hTimerEvent = NULL; return S_OK; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // connectors events //\_ void CMotionSensor::OnAddConnector( IConnector* pIConnector ) { _VerifyRV( pIConnector ); CLSID guidDataType; _ComVerifyRV( pIConnector->get_DataType( &guidDataType ) ); if ( guidDataType == GUID_BooleanDataType ) { CLSID clsid; _ComVerifyRV( pIConnector->get_ClassType( &clsid ) ); if ( clsid == CLSID_ChannelInputConnector ) _ComVerifyRV( DoAdvise( pIConnector, IID__IInputConnectorEvents ) ) else if ( clsid == CLSID_ChannelOutputConnector ) _ComVerifyRV( DoAdvise( pIConnector, IID__IOutputConnectorEvents ) ) _ComVerifyRV( DoAdvise( pIConnector, IID__IConnectorEvents ) ); } } void CMotionSensor::OnRemoveConnector( IConnector* pIConnector ) { _VerifyRV( pIConnector ); CLSID guidDataType; _ComVerifyRV( pIConnector->get_DataType( &guidDataType ) ); if ( guidDataType == GUID_BooleanDataType ) { CLSID clsid; _ComVerifyRV( pIConnector->get_ClassType( &clsid ) ); if ( clsid == CLSID_ChannelInputConnector ) DoUnadvise( pIConnector, IID__IInputConnectorEvents ); else if ( clsid == CLSID_ChannelOutputConnector ) DoUnadvise( pIConnector, IID__IOutputConnectorEvents ); DoUnadvise( pIConnector, IID__IConnectorEvents ); } } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // interface IStructureItem //\_ STDMETHODIMP CMotionSensor::Init() { if ( !m_aroStates.size() ) { CComPtr oState; _ComVerifyRS( CreateAppObject( CLSID_MotionSensorState, &oState, GetApplication(), (IAppObject*) this ) ); AddInitialState( oState ); } _ComVerifyRS( CreateAppObject( CLSID_MathFactory, &m_oMath, GetApplication(), (IAppObject*) this ) ); // start timer, we need frames not so often _ComVerifyRS( SetupFPSTimer() ); StartProcessThread(); return IStructureItemImpl::Init(); } STDMETHODIMP CMotionSensor::PostInit() { if ( !m_bsSIName.Length() ) _ComVerifyRS( LoadString( 1, &m_bsSIName ) ); _ComCallRH( PostInitChannelObject() ); _ComVerifyRS( DoAdvise( m_oChannel, IID__IChannelEvents ) ); _ComVerifyRS( m_oChannel->get_TransferMode( &m_TransferMode ) ); return IStructureItemImpl::PostInit(); } STDMETHODIMP_(void) CMotionSensor::PreClose() { DoUnadvise(); UninitChannelObject(); IStructureItemImpl::PreClose(); Lock(); m_oChannel.Release(); Unlock(); } STDMETHODIMP_(void) CMotionSensor::Close() { StopProcessThread(); CleanStates(); m_oMath.Release(); IStructureItemImpl::Close(); } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // Threat helpers //\_ BOOL CMotionSensor::StartProcessThread() { StopProcessThread(); UINT nThreadID = 0; ::ResetEvent( m_hStopProcess ); _VerifyRS( m_hProcessThread = (HANDLE) _beginthreadex( NULL, 0, ProcessData, this, 0, &nThreadID ) ); return m_hProcessThread != NULL; } void CMotionSensor::StopProcessThread() { // If thread was started, try to stop it. if ( m_hProcessThread ) { ::SetEvent( m_hStopProcess ); DWORD dwResult = ::WaitForSingleObject( m_hProcessThread, INFINITE ); // Terminate thread if it was not finished. if ( WAIT_TIMEOUT == dwResult ) // Manualy terminate thread. ::TerminateThread( m_hProcessThread, 0 ); // Close thread handle. ::CloseHandle( m_hProcessThread ); m_hProcessThread = NULL; } } UINT CMotionSensor::ProcessData( void* _pThis ) { CMotionSensor* pThis = reinterpret_cast( _pThis ); CoInitialize( NULL ); try { HANDLE arhWait[] = { pThis->m_hStopProcess, pThis->m_hProcessImage }; HANDLE arhWait2[] = { pThis->m_hStopProcess, pThis->m_hVideoOutReady }; // whait for some event while ( WAIT_OBJECT_0 != ::WaitForMultipleObjects( 2, arhWait, FALSE, INFINITE ) ) { ResetEvent( pThis->m_hProcessImage ); pThis->Lock(); CComQIPtr oState(pThis->m_oStateActive); pThis->Unlock(); if (oState) { BOOL bActive; _ComVerifyES( oState->get_ObjectActivity( &bActive ) ); // process data if neded if (bActive && (WAIT_OBJECT_0 == WaitForSingleObject(pThis->m_hTimerEvent,0))) { ResetEvent( pThis->m_hTimerEvent ); // store data pointer for future processing CDataObjects arImages( pThis->m_oCurFrame ); CDataObjects arMasks( pThis->m_oCurFrame ); // some processing int nPrevSample = pThis->m_nCurSample; pThis->m_nCurSample=(pThis->m_nCurSample+1)%2; C2DArray &oCurSample=pThis->m_arSamples[pThis->m_nCurSample]; _ComVerifyES(pThis->PreProcessImage(arImages[0],oCurSample)); // perform compare if (nPrevSample!=-1) { C2DArray &oPrevSample=pThis->m_arSamples[nPrevSample]; CComQIPtr oProperties(oState); long nColorSensivity, nBrightnessSensivity; _ComVerifyES(oProperties->get_ColorSensivity(&nColorSensivity)); _ComVerifyES(oProperties->get_BrightnesSensivity(&nBrightnessSensivity)); nColorSensivity = msb_MaxSensivity-nColorSensivity; nBrightnessSensivity = msb_MaxSensivity - nBrightnessSensivity; int nWidth = oCurSample.GetWidth(); int nHeight = oCurSample.GetHeight(); int nDiffSamples=0,nTotalSamples=0; // get surveliance areas vector < vector< POINT > > arPolyMasks; if (arMasks.size()) { arPolyMasks.resize(arMasks.size()); for (int i=0; iget_PointsCnt(&nMaskPointCount)); arPolyMasks[i].resize(nMaskPointCount); if (nMaskPointCount) arMasks[0]->GetPoints(&nMaskPointCount,&arPolyMasks[i].front()); } } // now perform compare for (int x=0; xm_nResample+pThis->m_nResample/2; ptHit.y=y*pThis->m_nResample+pThis->m_nResample/2; BOOL bInPoly; for (int i=0; i< arPolyMasks.size(); i++) { _ComVerifyES(pThis->m_oMath->PtInPoly(arPolyMasks[i].size(),&arPolyMasks[i].front(),&ptHit,&bInPoly)); if ( bInPoly ) { bNeedAnalyze=TRUE; break; } } } if (bNeedAnalyze) { SAMPLEDESC prev, cur; oPrevSample.Get(x,y,prev); oCurSample.Get(x,y,cur); BYTE Y1,Y2,U1,U2,V1,V2; // convert to YUV Y1=(19660*prev.G+9830*prev.R+3277*prev.B)>>15; U1=prev.B-Y1; V1=prev.R-Y1; Y2=(19660*cur.G+9830*cur.R+3277*cur.B)>>15; U2=prev.B-Y2; V2=prev.R-Y2; // compare if (abs(Y1-Y2)>nBrightnessSensivity || abs(U1-U2)>nColorSensivity || abs(V1-V2)>nColorSensivity ) nDiffSamples++; nTotalSamples++; } } // analyze results double C=double(nDiffSamples)/nTotalSamples; BOOL bStateChanged=FALSE; long nSensivity, nRateLag; _ComVerifyES(oProperties->get_Sensivity(&nSensivity)); _ComVerifyES(oProperties->get_ResponceDelay(&nRateLag)); double MinRatio =0.02+0.14* (double(msb_MaxSensivity-nSensivity)/msb_MaxSensivity); // check need or not to change state if (C>MinRatio) { if (nRateLag>=pThis->m_nAccMoving) { if (!pThis->m_bMotion) { pThis->m_bMotion = TRUE; bStateChanged = TRUE; } } else pThis->m_nAccMoving++; } else { if (pThis->m_nAccMoving==0) { if (pThis->m_bMotion) { pThis->m_bMotion = FALSE; bStateChanged = TRUE; } } else pThis->m_nAccMoving--; } // trye to imedially send result if (bStateChanged && pThis->m_oSensorOutput->IsReady()==S_OK) { CComPtr oData; _ComVerifyES( CreateAppObject( CLSID_BooleanData, &oData, pThis->GetApplication(), (IAppObject*) pThis ) ); oData->put_Value( pThis->m_bMotion ); pThis->m_oSensorOutput->put_Data( oData ); } } } } // whait while we can send data if ( WAIT_OBJECT_0 != ::WaitForMultipleObjects( 2, arhWait2, FALSE, INFINITE ) ) { // send data pThis->m_oVideoOutput->put_Data( pThis->m_oCurFrame ); // get ready for new data if (pThis->m_TransferMode == ctmParallel) pThis->m_oVideoInput->put_Ready(TRUE); }; } } catch( HRESULT hr ) { if ( FAILED( hr ) ) ::MessageBox( NULL, GetErrorDescription().c_str(), "Error", MB_OK|MB_ICONEXCLAMATION ); } CoUninitialize(); return 0; } HRESULT CMotionSensor::PreProcessImage( CComPtr oImageData, C2DArray &arSamples ) { _VerifyRS( oImageData ); long nImageWidth, nImageHeight, nBpp; _ComVerifyRS( oImageData->get_Width( &nImageWidth ) ); _ComVerifyRS( oImageData->get_Height( &nImageHeight ) ); _ComVerifyRS( oImageData->get_BPP( &nBpp ) ); Lock(); CComQIPtr oProperties(m_oStateActive); Unlock(); long nResolution; _ComVerifyRS(oProperties->get_Sensivity(&nResolution)); nResolution = 16+ 32*(double(nResolution)/msb_MaxSensivity); int nBpLine=nImageWidth*nBpp/8; m_nResample = nImageWidth/nResolution; arSamples.NewSize(nImageWidth/m_nResample+(nImageWidth%m_nResample?1:0), nImageHeight/m_nResample+(nImageHeight%m_nResample?1:0)); BYTE *pByteBuf; _ComVerifyRS( oImageData->LockBuffer( (void**) &pByteBuf ) ); for (int y=0,nSampleY=0;yUnlockBuffer() ); return S_OK; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // _IInputConnectorEvents events //\_ STDMETHODIMP_(void) CMotionSensor::OnDataReceived(IInputConnector * pIConnector, IUnknown* pDataUnk) { _VerifyRV(pIConnector); GUID guidDataType; pIConnector->get_DataType(&guidDataType); if ( guidDataType == GUID_BooleanDataType ) { ProcessStateInputConnectorData( pIConnector, pDataUnk ); return; } AUTO_LOCKER; BOOL bReady, bRequest; _VerifyRV(pIConnector); pIConnector->get_Ready(&bReady); pIConnector->get_Request(&bRequest); // if this input not ready simply return with no doubts if (!bReady) return; long nID; pIConnector->get_ID(&nID); // if we are in request mode clear this flag if (bRequest) pIConnector->put_Request(FALSE); pIConnector->put_Ready(FALSE); if (InlineIsEqualGUID(guidDataType,GUID_VideoDataType)) { m_oCurFrame = pDataUnk; SetEvent( m_hProcessImage ); } } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // _IOutputConnectorEvents //\_ STDMETHODIMP_(void) CMotionSensor::OnStateChanged(/*[in]*/ IOutputConnector* pIConnector ) { _VerifyRV( pIConnector ); if (m_oSensorOutput.IsEqualObject(pIConnector)) { // if data are ready, we need to send it if ( pIConnector->IsReady() == S_OK && pIConnector->IsRequest() == S_OK) { CComPtr oData; _ComVerifyRV( CreateAppObject( CLSID_BooleanData, &oData, GetApplication(), (IAppObject*) this ) ); oData->put_Value( m_bMotion ); pIConnector->put_Data( oData ); }; } if (m_oVideoOutput.IsEqualObject(pIConnector)) { // we are passive object, retranslate request to previouse object if (pIConnector->IsRequest() == S_OK) m_oVideoInput->put_Request(TRUE); // if data are ready, we need to send it if ( pIConnector->IsReady() == S_OK ) { SetEvent( m_hVideoOutReady ); if (m_TransferMode == ctmWaitComplete) m_oVideoInput->put_Ready(TRUE); } else ResetEvent ( m_hVideoOutReady ); } } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // _IConnectorEvents //\_ STDMETHODIMP_(VOID) CMotionSensor::OnConnected(IConnector * pIConnector) { if ( pIConnector->IsConnectorInput() == S_OK ) { CComQIPtr oInputConnector = pIConnector; _VerifyRV( oInputConnector ); oInputConnector->put_Ready(TRUE); oInputConnector->put_Request(TRUE); } if (m_oVideoOutput.IsEqualObject(pIConnector)) { if ( m_oVideoOutput->IsReady() == S_OK ) SetEvent( m_hVideoOutReady ); else ResetEvent ( m_hVideoOutReady ); } } STDMETHODIMP_(VOID) CMotionSensor::OnDisconnected(IConnector * pIConnector) { if ( m_oVideoOutput.IsEqualObject(pIConnector) ) ResetEvent ( m_hVideoOutReady ); } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // interface _IChannelEvents //\_ STDMETHODIMP_(void) CMotionSensor::OnTransferModeChanged( CHANNEL_MODE newMode ) { AUTO_LOCKER; m_TransferMode = newMode; return; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // interface _IMotionSensorStateEvents //\_ STDMETHODIMP_(void) CMotionSensor::OnDetectionRateChanged(IMotionSensorProperties * pState) { _VerifyRV( pState ); _ComVerifyRV( pState->get_MaxDetectionRate(&m_nFPS) ); SetupFPSTimer(); return; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // interface IPersistStream //\_ STDMETHODIMP CMotionSensor::Save(IStream *pStm, BOOL fClearDirty) { _ComCallRH( SaveSignature( pStm, "MOTIONSENSOR" ) ); _ComCallRH( SaveVersion( pStm, 1 ) ); _ComVerifyRS( SISave( pStm ) ); _ComVerifyRS( IChannelObjectImpl::Save( pStm ) ); _ComCallRH( m_bsSIName.WriteToStream( pStm ) ); _ComCallRH( m_bsSIDescr.WriteToStream( pStm ) ); _ComCallRH( IChannelObjectImpl::SaveStates( pStm ) ); return S_OK; } STDMETHODIMP CMotionSensor::Load(IStream *pStm) { try { _VerifyRS( CheckSignature( pStm, "MOTIONSENSOR" ) ); switch ( LoadVersion( pStm ) ) { case 0: return LoadVersion0( pStm ); case 1: return LoadVersion1( pStm ); } } catch ( HRESULT hr ) { return hr; } _ComVerifyRS( E_UNEXPECTED ); } STDMETHODIMP CMotionSensor::LoadVersion0(IStream *pStm) { _ComVerifyRS( SILoad( pStm ) ); _ComVerifyRS( IChannelObjectImpl::Load( pStm ) ); CComBSTR bsSIName; _ComCallRH( bsSIName.ReadFromStream( pStm ) ); _ComVerifyRS( put_SIName( bsSIName ) ); return S_OK; } STDMETHODIMP CMotionSensor::LoadVersion1(IStream *pStm) { _ComVerifyRS( SILoad( pStm ) ); _ComVerifyRS( IChannelObjectImpl::Load( pStm ) ); CComBSTR bsSIName, bsSIDescr; _ComCallRH( bsSIName.ReadFromStream( pStm ) ); _ComCallRH( bsSIDescr.ReadFromStream( pStm ) ); _ComVerifyRS( put_SIName( bsSIName ) ); _ComVerifyRS( put_SIDescription( bsSIDescr ) ); _ComCallRH( IChannelObjectImpl::LoadStates( pStm ) ); return S_OK; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // ICloneObject //\_ STDMETHODIMP CMotionSensor::Assign( IUnknown* pSourceObj ) { if( pSourceObj == NULL ) return E_POINTER; CComQIPtr< ICloneObject > oCloneObject( pSourceObj ); if( oCloneObject==NULL ) return E_NOINTERFACE; // check clone id CLSID clsid; _ComVerifyRS( oCloneObject->get_ObjectCLSID(&clsid) ); _VerifyRS( InlineIsEqualGUID(clsid,GetObjectCLSID()) ); _ComCallRH( AssignPart( IID_IStructureItem, pSourceObj) ); _ComCallRH( AssignPart( IID_IGraphicsChannelObject, pSourceObj ) ); _ComCallRH( AssignPart( IID_IMultiStateManager, pSourceObj ) ); return S_OK; } STDMETHODIMP CMotionSensor::AssignPart( REFIID riid, IUnknown *pSource ) { if ( riid == IID_IStructureItem ) return AssignIStructureItem( pSource ); if ( riid == IID_IGraphicsChannelObject ) return AssignIGraphicsChannelObject( pSource ); if ( riid == IID_IMultiStateManager ) return AssignIMultiStateManager( pSource ); return E_FAIL; } STDMETHODIMP CMotionSensor::EraseCloneHistory() { _ComCallRH( ICloneObjectImpl::EraseCloneHistory() ); EraseStatesCloneHistory(); return S_OK; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // _IMultiStateEvents //\_ void CMotionSensor::_OnActiveStateChanged( IUnknown* pStateUnk ) { DoUnadvise( IID__IMotionSensorStateEvents ); _ComVerifyRV( DoAdvise( pStateUnk, IID__IMotionSensorStateEvents ) ); CComQIPtr oSettings = pStateUnk; _VerifyRV( oSettings ); // get on the fly data _ComVerifyRV( oSettings->get_MaxDetectionRate(&m_nFPS) ); SetupFPSTimer(); } //\/\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // IChannelMenuHandler interface //\_ STDMETHODIMP CMotionSensor::ProcessChannelEditorContextMenu( IN IVssContextMenu* pIVssContextMenu ) { return AddStatesToContextMenu( pIVssContextMenu, this ); } //\/\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // IChannelMenuHandler interface //\_ STDMETHODIMP CMotionSensor::ExecuteCommand( UINT nID ) { return ShowProperties4State( nID ); } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // helper functions //\_ HRESULT CMotionSensor::SetupFPSTimer() { if (m_nTimerID) timeKillEvent(m_nTimerID); double timer=m_nFPS>1?(1/double(m_nFPS))*1000:1000; m_nTimerID = timeSetEvent(timer,0,LPTIMECALLBACK(m_hTimerEvent),NULL,TIME_PERIODIC|TIME_CALLBACK_EVENT_SET); if (m_nTimerID) return S_OK; else return E_FAIL; }