您的位置:首页 > 运维架构

基于OpenCV的视频图像组态 (2) :动画总体

2017-10-07 19:41 609 查看
写在前面

本系列博客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: drgraph@qq.com

需求

广告播放,一个主要需求是要以多种效果来展示素材。

闭门造车不是一个好选项,可以参照成熟专业软件的实现方式。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;

}


剩下的事情就是针对各动画类型进行细化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: