zoukankan      html  css  js  c++  java
  • 基于OpenCV的视频图像组态 (1) :时钟

    写在前面

    本系列博客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="resclockackground.png" width="1"/>
    
        <item angle="284" centerx="true" centery="true" height="1" name="hour" picfilename="resclockHour.png" width="0"/>
    
        <item angle="176" centerx="true" centery="true" height="1" name="minute" picfilename="resclockMinute.png" width="0"/>
    
        <item angle="150" centerx="true" centery="true" height="1" name="second" picfilename="resclockSecond.png" width="0"/>
    
        <item centerx="true" centery="true" height="1" picfilename="resclockHighlight.png" width="1"/>
    
    </GdiMeta>

    既然是自己用,那就先定好格式。几张图片就用几个节点。每个节点含以下属性:

    picfilename为图片文件名,采用相对路径,也可以是绝对路径

    angle为旋转角度,指绕本图像中心的旋转角度

    centerx、centery为横向、纵向中心对齐标志

    height、width为高宽比例,指该图形在最终图形中所占的高宽比例。

    name为本图片对应名称,后续可以根据该名称进行相应值的修改。

    思路理清了,模块化也就容易了,头文件        

    c

    lass 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 = 100
    
    virtual 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("
    ");
    
        return result;
    
    }
    
     
    
    void __fastcall TCbwGdiMeta::SetContent(UnicodeString value) {
    
        FXmlContent->ReadFromString(value);
    
    }
    
     
    
    void __fastcall TCbwGdiMeta::SetXmlContent(CbwXmlNode * node) {
    
        if(node != FXmlContent) {
    
            UnicodeString content = node->GetHint("
    ");
    
            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";
    
    }

    现在可以完全支持模拟时钟。对于数字时钟、日历、天气预报等,稍后再整理。

  • 相关阅读:
    Mysql主键索引、唯一索引、普通索引、全文索引、组合索引的区别
    centos7下安装phpmyadmin
    centos7下apache启动报错记录
    Centos7 升级php版本到php7
    centos7下安装composer和git
    git推送新项目到github
    git使用入门
    laravel5.5学习2-路由系统
    laravel5.5入门-安装和认证
    laravel的monolog使用
  • 原文地址:https://www.cnblogs.com/drgraph/p/7630147.html
Copyright © 2011-2022 走看看