基于OpenCV的视频图像组态 (1) :时钟
来源:互联网 发布:flower dance知乎 编辑:程序博客网 时间:2024/04/30 08:12
写在前面
本系列博客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等多媒体素材,还希望能看到一些小插件,看到比如时钟、天气预报等内容。当然,在界面定制的时候,指定一个小块显示相应内容就OK。
初步解决
先来处理时钟。
既然先说了小插件,就先参考一下WINDOWS中的小工具。
当然,时钟会有很多风格,先出一版这种效果的。
其实就是一个画图操作,不过想要改这个UI效果,如果用代码来实现,好象还得要比较繁琐的支持,在网上看到一种用图片叠加起来的时钟。
把几个图片素材下载下来,再重新命名一下。
最终就是采用Gdiplus技术,把这几张图片按顺序画出:
FGraphics->DrawImage(&image, x, y, w, h);
该转的就转一下。
Gdiplus::Bitmap * bitmap = RotateImage(&image, locked_theta, w, h);
float dx = x + w / 2 - bitmap->GetWidth() / 2;
float dy = y + h / 2 - bitmap->GetHeight() / 2;
FGraphics->DrawImage(bitmap, dx, dy, bitmap->GetWidth(), bitmap->GetHeight());
delete bitmap;
好象也没有什么好说的。可以拼成这种效果
模块化
在实现的过程中,突然发现,这种由多张图片叠加组成的场合还有一些,干脆提炼成一个模块,后续可以直接调用。
这里面最主要的是配置,每张图片都可以指定。最容易想到的解决方案就是采用XML方式来定义。
比如上面的时钟,可以用XML定义如下:
<GdiMeta> <item angle="0" centerx="true" centery="true" height="1" picfilename="res\clock\background.png" width="1"/> <item angle="284" centerx="true" centery="true" height="1" name="hour" picfilename="res\clock\Hour.png" width="0"/> <item angle="176" centerx="true" centery="true" height="1" name="minute" picfilename="res\clock\Minute.png" width="0"/> <item angle="150" centerx="true" centery="true" height="1" name="second" picfilename="res\clock\Second.png" width="0"/> <item centerx="true" centery="true" height="1" picfilename="res\clock\Highlight.png" width="1"/></GdiMeta>
既然是自己用,那就先定好格式。几张图片就用几个节点。每个节点含以下属性:
picfilename为图片文件名,采用相对路径,也可以是绝对路径
angle为旋转角度,指绕本图像中心的旋转角度
centerx、centery为横向、纵向中心对齐标志
height、width为高宽比例,指该图形在最终图形中所占的高宽比例。
name为本图片对应名称,后续可以根据该名称进行相应值的修改。
思路理清了,模块化也就容易了,头文件
class TCbwGdiMeta : public TRectangle {typedef TRectangle inherited;Gdiplus::Graphics * FGraphics;CbwXmlNode * FXmlContent;void __fastcall DrawXmlNode(CbwXmlNode * xmlNode, Gdiplus::RectF rf, Gdiplus::Graphics * FGraphics, Gdiplus::Brush * brush, Gdiplus::Pen * pen);UnicodeString __fastcall GetContent();void __fastcall SetContent(UnicodeString value);void __fastcall SetXmlContent(CbwXmlNode * node);public:CBW_META_OBJECT(TCbwGdiMeta, TRectangle);virtual void __fastcall DoDraw(); // 画出对象virtual bool __fastcall PreDraw(); void __fastcall SetProperty(UnicodeString name, UnicodeString value); // 设置XML节点属性,如 hour.angle = 100virtual void __fastcall DoAddToXmlNode(CbwXmlNode * node);virtual void __fastcall DoGetFromXmlNode(CbwXmlNode * node, int& index); __published:__property UnicodeString Content = { read = GetContent, write = SetContent };__property CbwXmlNode * XmlContent = { read = FXmlContent, write = SetXmlContent };};
源文件
__fastcall TCbwGdiMeta::~TCbwGdiMeta() { delete FXmlContent;} void __fastcall TCbwGdiMeta::Initial() {ObjectClassType = cctGdiMeta;Name = THelper::FormatString("GdiMeta%d", MetaIndex[int(ObjectClassType)]);++MetaIndex[int(ObjectClassType)];FXmlContent = new CbwXmlNode("GdiMeta");} bool __fastcall TCbwGdiMeta::PreDraw() {if (!inherited::PreDraw())return false; return true;} void __fastcall TCbwGdiMeta::DoDraw() {FGraphics = GetGraphicsForRotate(Canvas); FGraphics->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);//SmoothingModeHighQuality);TPoint p0 = TPoint(Points[0].x * Ratio, Points[0].y * Ratio);TPoint p1 = TPoint(Points[1].x * Ratio, Points[1].y * Ratio);TRect r = TTypeConvert::Points2Rect(p0, p1);Gdiplus::Brush * brush = BrushByData(BrushData, r);Gdiplus::RectF rf(r.left, r.top, r.Width(), r.Height()); Gdiplus::Pen gpen(Color2GPColor(PenData->Color), PenData->Width * Ratio);for(int i = 0; i < FXmlContent->ElementNumber; ++i) {CbwXmlNode * node = FXmlContent->Elements(i); DrawXmlNode(node, rf, FGraphics, brush, &gpen);} if(brush) delete brush;delete FGraphics;} void __fastcall TCbwGdiMeta::DrawXmlNode(CbwXmlNode * xmlNode, Gdiplus::RectF rf, Gdiplus::Graphics * FGraphics, Gdiplus::Brush * brush, Gdiplus::Pen * pen) {UnicodeString picName = xmlNode->AttributeValueByName("picFileName");if(picName.Length()) {if(picName.Pos(L":") == 0)picName = THelper::File::GetApplicationPath() + picName;if(FileExists(picName)) { Gdiplus::Image image(picName.w_str(), false); float x = xmlNode->AttributeValueByName("x", "0").ToDouble(); float y = xmlNode->AttributeValueByName("y", "0").ToDouble(); float w = xmlNode->AttributeValueByName("width", "0").ToDouble(); float h = xmlNode->AttributeValueByName("height", "0").ToDouble(); double locked_theta = xmlNode->AttributeValueByName("angle", "0").ToDouble(); while(locked_theta >= 360) locked_theta -= 360; while(locked_theta < 0) locked_theta += 360; double width = rf.Width, height = rf.Height; x *= width; w *= width; y *= height; h *= height; if(w == 0) // 未定义宽度,则由原始宽度计算 w = image.GetWidth() * Ratio; if(h == 0) // 未定义高度,则由原始高度计算 h = image.GetHeight() * Ratio; if(xmlNode->BoolAttributeValueByName("centerx", "true")) x = (width - w) / 2; if(xmlNode->BoolAttributeValueByName("centery", "true")) y = (height - h) / 2; if(locked_theta) { Gdiplus::Bitmap * bitmap = RotateImage(&image, locked_theta, w, h); float dx = x + w / 2 - bitmap->GetWidth() / 2; float dy = y + h / 2 - bitmap->GetHeight() / 2; FGraphics->DrawImage(bitmap, dx, dy, bitmap->GetWidth(), bitmap->GetHeight()); delete bitmap;} else FGraphics->DrawImage(&image, x, y, w, h);}}} void __fastcall TCbwGdiMeta::SetProperty(UnicodeString pname, UnicodeString value) { UnicodeString name = THelper::String::GetStringAt(pname, ".", 0); UnicodeString attr = THelper::String::GetStringAt(pname, ".", 1); CbwXmlNode * destNode = FXmlContent->NodeByAttribute("name", name); if(destNode) destNode->AddAttribute(attr, value);} void __fastcall TCbwGdiMeta::DoAddToXmlNode(CbwXmlNode * node) { inherited::DoAddToXmlNode(node); CbwXmlNode * thisNode = node->LastNode; thisNode->AddElement(FXmlContent->Clone());} void __fastcall TCbwGdiMeta::DoGetFromXmlNode(CbwXmlNode * node, int& index) { inherited::DoGetFromXmlNode(node, index); CbwXmlNode * thisNode = node->LastNode; FXmlContent->Assign(thisNode);} UnicodeString __fastcall TCbwGdiMeta::GetContent() { UnicodeString result = FXmlContent->GetHint("\n"); return result;} void __fastcall TCbwGdiMeta::SetContent(UnicodeString value) { FXmlContent->ReadFromString(value);} void __fastcall TCbwGdiMeta::SetXmlContent(CbwXmlNode * node) { if(node != FXmlContent) { UnicodeString content = node->GetHint("\n"); SetContent(content); } Draw();}
剩下的就是实时更新时钟,直接更新即可。
void __fastcall TCbwGraphForm_Player::UpdateTimeControls() { CBW_ITERATOR(CbwObjects, Objects) { TCbwGdiMeta * gdiMeta = dynamic_cast<TCbwGdiMeta *>(*it); if(gdiMeta && CanObjectBeVisible(gdiMeta)) { unsigned short Year, Month, Day, Hour, Minute, sec, msec; DecodeTime(Now(), Hour, Minute, sec, msec);double minute = Minute + sec / 60.0; double hour = (Hour + minute / 60.0) * 30; minute = minute * 6; sec *= 6; gdiMeta->SetProperty("Hour.angle", hour); gdiMeta->SetProperty("Minute.angle", minute); gdiMeta->SetProperty("Second.angle", sec); gdiMeta->Draw(); } }}
如此这般,在运行时就可以看到实时时钟效果
属性处理
为了更通用地处理,再实现一个兼容xml内容的属性浏览器,主要需要增加两个属性类型:
头文件:
class CbwXmlNodeAttributePropertyItem : public OrdinalPropertyItem { typedef OrdinalPropertyItem inherited; CbwXmlNode * FDestNode; virtual void __fastcall GetPropertyValue(); virtual void __fastcall SetValue(Variant var);public: __fastcall CbwXmlNodeAttributePropertyItem(TObject * object, UnicodeString propertyName, UnicodeString displayName = "", bool v = true);}; class CbwXmlNodePropertyItem : public ObjectPropertyItem { typedef ObjectPropertyItem inherited; CbwXmlNode * FDestNode; virtual void __fastcall AddItems(TList * item); virtual void __fastcall GetPropertyValue(); virtual void __fastcall SetStringList(TStringList * list);public: __fastcall CbwXmlNodePropertyItem(TObject * object, UnicodeString propertyName, UnicodeString displayName = "", bool v = true);};
源代码:
__fastcall CbwXmlNodeAttributePropertyItem::CbwXmlNodeAttributePropertyItem (TObject * object, UnicodeString propertyName, UnicodeString displayName, bool v) : OrdinalPropertyItem(object, propertyName, displayName, v) {} void __fastcall CbwXmlNodeAttributePropertyItem::GetPropertyValue() { FDestNode = dynamic_cast<CbwXmlNode *>(Object); if(FDestNode) PropertyValue = FDestNode->AttributeValueByName(PropertyName);} void __fastcall CbwXmlNodeAttributePropertyItem::SetValue(Variant var) { if(FDestNode) { FDestNode->AddAttribute(PropertyName, var); CbwXmlNodePropertyItem * parent = dynamic_cast<CbwXmlNodePropertyItem *>(ParentItem); CbwXmlNode * node = FDestNode;//->ParentNode; while(parent && node) { if(IsPublishedProp(parent->Object, parent->PropertyName)) { SetObjectProp(parent->Object, parent->PropertyName, node); break; } parent = dynamic_cast<CbwXmlNodePropertyItem *>(parent->ParentItem); node = node->ParentNode;}}} void __fastcall CbwXmlNodePropertyItem::SetStringList(TStringList * list) { } void __fastcall CbwXmlNodePropertyItem::GetPropertyValue() {PropertyValue = PropertyName;} void __fastcall CbwXmlNodePropertyItem::AddItems(TList * item) { int count = item->Count; for (int i = 0; i < count; i++) { CbwPropertyItem * cItem = (CbwPropertyItem*)(item->Items[i]); delete cItem; } delete item;} __fastcall CbwXmlNodePropertyItem::CbwXmlNodePropertyItem (TObject * object, UnicodeString propertyName, UnicodeString displayName, bool v) : ObjectPropertyItem(object, propertyName, displayName, v) { FreeAllItem(); FDestNode = dynamic_cast<CbwXmlNode *>(object); if(!FDestNode) FDestNode = dynamic_cast<CbwXmlNode *>(GetObjectProp(object, propertyName)); if(FDestNode) { StringList->Clear(); int count = FDestNode->ElementNumber + FDestNode->Attributes.size(); if(count) { cItemList = new PPropertyItem[count]; int index = FDestNode->ElementNumber; CBW_ITERATOR_MAP(UnicodeString, UnicodeString, FDestNode->Attributes) cItemList[index++] = new CbwXmlNodeAttributePropertyItem(FDestNode, it->first); for(int i = 0; i < FDestNode->ElementNumber; ++i) { CbwXmlNode * node = FDestNode->Elements(i); UnicodeString name = node->Name; if(node->ContainAttribute("Name")) name = node->AttributeValueByName("Name"); cItemList[i] = new CbwXmlNodePropertyItem(node, name); } for(int i = 0; i < count; ++i) { StringList->Add("empty"); cItemList[i]->ParentItem = this;} } } PropertyValue = "XmlNode";}
现在可以完全支持模拟时钟。对于数字时钟、日历、天气预报等,稍后再整理。
- 基于OpenCV的视频图像组态 (1) :时钟
- 基于OpenCV的视频图像组态 (2) :动画总体
- 基于OpenCV的视频图像组态 (4) :劈裂动画效果
- 基于OpenCV的视频图像组态 (5) :擦除动画效果
- 基于OpenCV的视频图像组态 (6): 形状动画效果
- 基于OpenCV的视频图像组态 (7) :轮子动画效果
- 基于OpenCV的视频图像组态 (9):CEF浏览器初步
- 基于OpenCV的视频图像组态 (14):音量控制
- 基于OpenCV的视频图像组态 (3):常见PPT动画1
- 基于OpenCV的视频图像组态 (8) :随机线条动画效果
- 基于OpenCV的视频图像组态 (10): CEF浏览器与图形软件互嵌
- 基于OpenCV的视频图像组态 (11): CEF浏览器与C++通信
- 基于OpenCV的视频图像组态 (12): 翻转式由远到近动画效果
- 基于OpenCV的视频图像组态 (13):VLC Player解码帧数据
- 基于OPENCV的视频图像处理算法和应用
- 基于OpenCV的图像检索系统
- 基于OpenCV的图像拼接
- 基于OpenCV的图像测量
- bzoj 1230: [Usaco2008 Nov]lites 开关灯
- Android学习-常见的UI控件 ToggleButton和CheckBox
- Socket通信
- HDU5988-Coding Contest
- Python2 字符串
- 基于OpenCV的视频图像组态 (1) :时钟
- 搬瓦工(bandwagonhost)一键安装Shadowsocks教程
- 幸运数
- Windows下Visual Studio配置OpenCV3
- Rust: codewars的Roman Numerals Encoder
- HDU
- JS 基础 —— ECMAScript 运算符 与 语句
- C++ 数据结构
- poj 1200 Crazy Search(疯狂搜索)