1. 程式人生 > >基於OpenCV的視頻組態 (2) :動畫總體

基於OpenCV的視頻組態 (2) :動畫總體

tag evo odm enc 底部 direction san 基本上 界面

寫在前面

本系列博客URL:

http://www.cnblogs.com/drgraph

http://blog.csdn.net/arwen

配套軟件下載地址:

http://www.czwenwu.com/YeeVingSetup.exe

配套軟件含三個可執行文件:YeeVingDriver.exe,YeeVingPlayer.exe,WatchDog.exe

其中,YeeVingDriver.exe是雙目觸控屏的驅動程序,內含鍵盤鼠標鉤子,安裝或運行的時候有可能會當成病毒。

WatchDog.exe是無人值守軟件

YeeVingPlayer.exe是廣告播放軟件客戶端。

本系列博客是在上述三個軟件研發過程中的片面記錄,基本上是屬於想到哪寫到哪的,不系統。主要目的是自己整理歸納一下,並期望與更多朋友交流。

QQ/微信:282397369

EMail: [email protected]

需求

廣告播放,一個主要需求是要以多種效果來展示素材。

閉門造車不是一個好選項,可以參照成熟專業軟件的實現方式。PPT裏的動畫就是一個好的可參考對象。

之前已經實現了各種PPT的動畫,今天稍微總結一下。

Ribbon界面

首先做PPT的動畫界面,這個用Ribbon風格控件,就是花點時間的事,沒太大的技術含量。

技術分享

最花時間的是找圖標,不過現在別的不好找,代碼、圖標卻是大把。

技術分享

花了半天時間,雖談不上是神似,也能算得上是形似的了。具體功能將逐個實現。

動畫基類

再看下PPT中的動畫屬性,其界面最終所圍繞的也就是這兩個

技術分享 技術分享

分解一下,設計幾個數據結構:

         typedefstruct tagEnhanceInfo { // 增強選項   

                   UnicodeStringSoundFileName; // 聲音文件

                   TEffect_Enhance_AfterPlayAfterPlayMode; // 動畫播放後處理方式

                   TColorAfterAnimationColor; // 變換為某種顏色,FAfterPlayMode為teapToColor有效
UnicodeStringAnimationText; // 動畫文本 intDelayPercent; // 字母之間延遲百分比 }ENHANCE_INFO; typedefstruct tagTimerInfo { // 計時選項 TEffect_Time_StartStartMode; // 開始選項 intDelay; // 延遲秒數 TEffect_Time_PeriodPeriodMode; // 期間選項 TEffect_Time_RepeatRepeatMode; // 重復選項 boolQuickBackAfterPlay; // 播完後快退 }TIMER_INFO; enum TEffect_Enhance_AfterPlay { // 增強選項-動畫後播放選項 teapToColor,// 變換為某種顏色 teapNoDarking,// 不變暗 teapHide,// 隱藏 teapHideAfterClick// 下次單擊後隱藏 }; enum TEffect_Time_Start { // 計時選項-開始選項 ttsClick,// 單擊時 ttsSameTimeAsPrevOne,// 與上一動畫同時 ttsAfterPrevOne// 上一動畫完成之後 }; enum TEffect_Time_Period { // 計時選項-期間 ttpVerySlow,// 非常慢 ttpSlow,// 慢速 ttpNormal,// 中速 ttpFast,// 快速 ttpVeryFast// 非常快 }; enum TEffect_Time_Repeat { // 計時選項-重復 ttrNone,// ttr2,// 2 ttr3,// 3 ttr4,// 4 ttr5,// 5 ttr10,// 10 ttrTillNextClick,// 直到下一次單擊 ttrTillEnd// 直到結束 }; enum CbwEffectType { // 效果類型枚舉量 cetBase= 0, // TCbwAnimationEffect cetAppear= 1, // TCbwAnimationEffect_Appear cetFadeOut= 2, // TCbwAnimationEffect_FadeOut cetFlyIn= 3, // TCbwAnimationEffect_FlyIn cetEnd }; enum CbwEffectDirection { // 動畫方向 cedFromBottom= 0, // 自底部 cedFromLeftBottom= 1, // 自左下部 cedFromLeft= 2, // 自左側 cedFromLeftTop= 3, // 自左上部 cedFromTop= 4, // 自頂部 cedFromRightTop= 5, // 自右上部 cedFromRight= 6, // 自右側 cedFromRightBottom= 7 // 自右下部 };

為了更好地模塊化,把對象相關的動畫屬性提取出來

          typedefstruct tagObjectMat {

                   TPointLeftTopPosition;

                   TCbwObject* Object;

                    cv::MatMat;

                   cv::MatMask;

                    void__fastcall BuildMask(int height, int width);

         }OBJECTMAT;

絕大部分動畫,是控制過程的顯示。由OpenCV技術來看,這個過程分為三塊:顯示區域、顯示內容、屏蔽內容。

這樣,可以設計動畫基類為:

/**

 *@class TCbwAnimationEffect

 *@brief 動畫基類

 *

 * 處理動畫基本內容

 *@author DrGraph

 *@version 1.0

 *@date 2017-10-07

 *@QQ: 282397369

 */

class TCbwAnimationEffect {

         bool__fastcall IsAtEnd();

         CbwObjectsFAnimationObjects; // 相關對象

   

         cv::Mat__fastcall Object2Mat(TCbwObject * object, double ratio = 1);

         int__fastcall GetRepeateTime();

protected:

         vector<OBJECTMAT>FDestMats;

         intFCurrentIndex; // 當前幀索引

         intFPeriodLength; // 周期長度,指動畫一個周期內的幀數

         intFPosition; // 當前位置,指動畫累加索引位置

         intFWidth, FHeight; // 長寬尺寸,指顯示限制

   

public:

         CbwEffectTypeEffectType;

         ENHANCE_INFOEnhanceOption; // 增強選項

         TIMER_INFOTimerOption; // 計時選項

   

         __fastcallTCbwAnimationEffect();

         staticTCbwAnimationEffect * Build();

   

         void__fastcall Assign(TCbwAnimationEffect * other);

   

         void__fastcall AddToXmlNode(CbwXmlNode * node);

         void__fastcall GetFromXmlNode(CbwXmlNode * node);

   

         void__fastcall First();

         void__fastcall Next();

         void__fastcall SetRelativeObject(CbwObjects relativeObjects,

                   TPaintBox* pb, TScrollBox * scrollBox);

         void__fastcall SetBounds(int width, int height);

   

         void__fastcall Draw(HWND wnd, BYTE * backData, int width, int height);

   

         cv::MatCurrentMat;

         __propertybool Eof = {read = IsAtEnd};

   

protected:

         virtualTRect __fastcall BuildDisplayRect(OBJECTMAT * m);

         virtualvoid __fastcall BuildDisplayMat(cv::Mat& destMat, cv::Mat& srcMat);

         virtualvoid __fastcall BuildMaskMat(cv::Mat& destMat, cv::Mat& srcMat);

};

   

核心實現代碼為:

__fastcallTCbwAnimationEffect::TCbwAnimationEffect() {

         TimerOption.StartMode= ttsClick;

         TimerOption.Delay= 0;

         TimerOption.PeriodMode= ttpNormal;

         TimerOption.RepeatMode= ttrNone;

         TimerOption.QuickBackAfterPlay= false;

         EffectType= cetBase;

}

   

TCbwAnimationEffect *TCbwAnimationEffect::Build() {

         returnnew TCbwAnimationEffect;

}

   

TRect __fastcallTCbwAnimationEffect::BuildDisplayRect(OBJECTMAT * m) {

         TRectresult(m->LeftTopPosition.x, m->LeftTopPosition.y,

                   m->LeftTopPosition.x+ m->Mat.cols, m->LeftTopPosition.y + m->Mat.rows);

         returnresult;

}

   

void __fastcall TCbwAnimationEffect::BuildDisplayMat(cv::Mat&destMat,

         cv::Mat&srcMat) {

         destMat= srcMat.clone();

}

   

void __fastcallTCbwAnimationEffect::BuildMaskMat(cv::Mat& destMat,

         cv::Mat&srcMat) {

         destMat= srcMat.clone();

}

   

void __fastcallTCbwAnimationEffect::Draw(HWND wnd, BYTE * backData, int width,

         intheight) {

         cv::MatbackGndMat(height, width, CV_8UC3); // 背景

         GlobalOpenCVObject->CopyRGBDatasToMat(backGndMat,backData, width,

                   height,true);

   

         CBW_ITERATOR(vector<OBJECTMAT>,FDestMats) {

                   OBJECTMAT* animationObject = &(*it);

                   //以下取得待顯示的區域、內容

                   TRectanimationDisplayRect = BuildDisplayRect(animationObject); // 目標區域

   

                   cv::MatanimationDisplayMat =

                            cv::Mat::zeros(animationDisplayRect.Height(),

                            animationDisplayRect.Width(),animationObject->Mat.type());

                   BuildDisplayMat(animationDisplayMat,animationObject->Mat); // 待顯示內容

   

                   cv::MatmaskMat = cv::Mat::zeros(animationObject->Mask.rows,

                            animationObject->Mask.cols,animationObject->Mask.type());

                   BuildMaskMat(maskMat,animationObject->Mask); // Mask內容

   

                   TRectsuitableDisplayRect; // 真正的目標顯示區域

                   suitableDisplayRect.left= max(0, int(animationDisplayRect.left));

                   suitableDisplayRect.top= max(0, int(animationDisplayRect.top));

                   suitableDisplayRect.right= min(int(animationDisplayRect.right), width);

                   suitableDisplayRect.bottom= min(int(animationDisplayRect.bottom),

                            height);

   

                   TRectsuitableRectInMat(0, 0, suitableDisplayRect.Width(),

                            suitableDisplayRect.Height());

                   intdeltaL =

                            max(0,int(suitableDisplayRect.left - animationDisplayRect.left));

                   if(suitableDisplayRect.left != animationDisplayRect.left) {

                            suitableRectInMat.left+= deltaL;

                            suitableRectInMat.right+= deltaL;

                   }

                   intdeltaT =

                            max(0,int(suitableDisplayRect.top - animationDisplayRect.top));

                   if(suitableDisplayRect.top != animationDisplayRect.top) {

                            suitableRectInMat.top+= deltaT;

                            suitableRectInMat.bottom+= deltaT;

                   }

   

                   cv::MatdestPartMat =

                            animationDisplayMat(cv::Rect(suitableRectInMat.left,

                            suitableRectInMat.top,suitableRectInMat.Width(),

                            suitableRectInMat.Height()));

                   cv::MatbackgndPartMat = // 目標背景區域相應矩陣

                            backGndMat(cv::Rect(suitableDisplayRect.left,

                            suitableDisplayRect.top,suitableDisplayRect.Width(),

                            suitableDisplayRect.Height()));

                   cv::MatmaskPartMat =

                            maskMat(cv::Rect(deltaL,deltaT, suitableDisplayRect.Width(),

                            suitableDisplayRect.Height()));

                   destPartMat.copyTo(backgndPartMat,maskPartMat);

         }

         GlobalOpenCVObject->PreviewMat(wnd,backGndMat, width, height);

}

   

void __fastcallTCbwAnimationEffect::Assign(TCbwAnimationEffect * other) {

         TimerOption.StartMode= other->TimerOption.StartMode;

         TimerOption.Delay= other->TimerOption.Delay;

         TimerOption.PeriodMode= other->TimerOption.PeriodMode;

         TimerOption.RepeatMode= other->TimerOption.RepeatMode;

         TimerOption.QuickBackAfterPlay= other->TimerOption.QuickBackAfterPlay;

}

   

bool __fastcallTCbwAnimationEffect::IsAtEnd() {

         boolresult = (FPosition >= FPeriodLength * GetRepeateTime());

         returnresult;

}

   

void __fastcall TCbwAnimationEffect::First(){

         FPosition= 0;

         FCurrentIndex= 0;

}

   

void __fastcall TCbwAnimationEffect::Next(){

         ++FPosition;

         FCurrentIndex= FPosition % FPeriodLength;

}

   

cv::Mat __fastcallTCbwAnimationEffect::Object2Mat(TCbwObject * object,

         doubleratio) {

         TCanvas* oldCanvas = object->Canvas;

         Graphics::TBitmap* bitmap = new Graphics::TBitmap;

         bitmap->PixelFormat= pf24bit;

         bitmap->Width= object->Width;

         bitmap->Height= object->Height;

   

         TCanvas* canvas = bitmap->Canvas;

         boolallowDraw = object->AllowDraw;

         boololdDrawBorderFlag = object->DrawBorderFlag;

         object->AllowDraw= false; {

                   TRestoreleft(object, "Left", 0);

                   TRestoretop(object, "Top", 0);

                   object->Canvas= canvas;

                   object->AllowDraw= allowDraw;

                   object->DrawBorderFlag= false;

                   object->Draw();

                   object->AllowDraw= false;

         }object->Canvas = oldCanvas;

         object->AllowDraw= allowDraw;

         object->DrawBorderFlag= oldDrawBorderFlag;

         BYTE* backData = THelper::Graphics::GetBitmapData(bitmap);

         cv::Matresult;

         GlobalOpenCVObject->CopyRGBDatasToMat(result,backData, bitmap->Width,

                   bitmap->Height,true);

         deletebitmap;

         deletebackData;

         returnresult;

}

   

void __fastcallTCbwAnimationEffect::OBJECTMAT::BuildMask(int height, int width)

{

         Mask= cv::Mat(height, width, CV_8UC1);

         BYTE* pSrc = Mat.data;

         BYTE* pDst = Mask.data;

         for(int i = height * width - 1; i >= 0; --i) {

                   BYTEB = *pSrc++;

                   BYTEG = *pSrc++;

                   BYTER = *pSrc++;

                   *pDst++= (B == 0xFF && G == 0xFF && R == 0xFF) ? 0 : 255;

         }

}

   

void __fastcallTCbwAnimationEffect::SetRelativeObject

         (CbwObjectsrelativeObjects, TPaintBox * pb, TScrollBox * scrollBox) {

         CBW_ITERATOR(CbwObjects,relativeObjects) {

                   cv::Matmat = Object2Mat(*it);

                   TPointlt = TPoint((*it)->Left, (*it)->Top);

                   lt= pb->ClientToScreen(lt);

                   lt= scrollBox->ScreenToClient(lt);

                   OBJECTMATm;

                   m.Mat= mat;

                   m.LeftTopPosition= lt;

                   m.Object= *it;

                   m.BuildMask((*it)->Height,(*it)->Width);

                   FDestMats.push_back(m);

         }

         FPeriodLength= 50;

         FCurrentIndex= 0;

         FPosition= 0;

}

   

void __fastcallTCbwAnimationEffect::SetBounds(int width, int height) {

         FWidth= width;

         FHeight= height;

}

調用

為方便調用處邏輯簡單,可采用:

typedef TCbwAnimationEffect *(*BuildEffectObject)(); // 智能構造函數


typedef std::map<int,BuildEffectObject>EffectObjectMap; // 構造各圖元對象映射
#define CREATEEFFECTOBJECTif(!CbwEffectObjectMap) CbwEffectObjectMap = new EffectObjectMap;  (*CbwEffectObjectMap)
…
         CREATEEFFECTOBJECT[cetBase]= TCbwAnimationEffect::Build;
         CREATEEFFECTOBJECT[cetAppear]= TCbwAnimationEffect_Appear::Build;
         CREATEEFFECTOBJECT[cetFadeOut]= TCbwAnimationEffect_FadeOut::Build;
         CREATEEFFECTOBJECT[cetFlyIn]= TCbwAnimationEffect_FlyIn::Build;

這樣,調用時,簡單處理即可:

         if(!(*CbwEffectObjectMap)[effectType]) {


                   THelper::Util::MessageBox(L"本動畫效果尚未實現,請稍候!", false);


             return;


         }


         TCbwAnimationEffect* effectItem = (*CbwEffectObjectMap)[effectType](); // 根據類型創建動畫對象

調用,先處理用戶選中某個對象後的動畫類型邏輯

void __fastcall TForm::AddEffect(TdxRibbonGalleryGroupItem*AItem) {

         if(cSelectedObjects->MetaNumber== 0) return;

         inteffectType = AItem->ImageIndex + 1;

         if(!(*CbwEffectObjectMap)[effectType]) {

                   THelper::Util::MessageBox(L"本動畫效果尚未實現,請稍候!", false);

             return;

         }

         TCbwAnimationEffect* effectItem = (*CbwEffectObjectMap)[effectType](); // 根據類型創建動畫對象

         effectItem->SetRelativeObject(cSelectedObjects->SubObjects,PaintBox, ScrollBox);

         effectItem->SetBounds(ScrollBox->Width,ScrollBox->Height);

         Graphics::TBitmap* bitmap = new Graphics::TBitmap;

         bitmap->PixelFormat= pf24bit;

         bitmap->Width= ScrollBox->Width;

         bitmap->Height= ScrollBox->Height;

   

         RECTdisplayRect = Rect(ScrollBox->HorzScrollBar->Position,

                   ScrollBox->VertScrollBar->Position,ScrollBox->HorzScrollBar->Position +

                   ScrollBox->Width,ScrollBox->VertScrollBar->Position +

                   ScrollBox->Height);

   

         Graphics::TBitmap* FPreviewBitmap = new Graphics::TBitmap;

         FPreviewBitmap->PixelFormat= pf24bit;

         FPreviewBitmap->Width= PaintBox->Width;

         FPreviewBitmap->Height= PaintBox->Height;

         TCanvas* canvas = FPreviewBitmap->Canvas;

         canvas->Rectangle(0,0, 10000, 10000);

         CBW_ITERATOR(CbwObjects,Objects)(*it)->Canvas = canvas;

   

         CBW_ITERATOR(CbwObjects,Objects) {

                   TCbwObject* object = *it;

                   if(!CanObjectBeVisible(object) || !object->CanContinueWithRect

                            (displayRect,CBW_CONTINUE_DRAW) || object->Selected)

                            continue;

                   object->Draw();

         }

         PostPaint(canvas);

         bitmap->Canvas->CopyRect(Rect(0,0, bitmap->Width, bitmap->Height), canvas,

                   displayRect);

         CBW_ITERATOR(CbwObjects,Objects)(*it)->Canvas = PaintBox->Canvas;

   

         TRestoreApplicationCurrentStatus(TGraphApp::CurrentStatus, cfsAnimation);

         BYTE* backData = THelper::Graphics::GetBitmapData(bitmap);

         effectItem->First();

         while(!effectItem->Eof){

                   effectItem->Draw(ScrollBox->Handle,backData, bitmap->Width, bitmap->Height);

                   effectItem->Next();

                   Sleep(10);

         }

         deletebackData;

         deleteFPreviewBitmap;

         deletebitmap;

         deleteeffectItem;

}

剩下的事情就是針對各動畫類型進行細化。

基於OpenCV的視頻組態 (2) :動畫總體