zoukankan      html  css  js  c++  java
  • 计算照片的面积(WPF篇)

    昨天,老周突发其想地给大伙伴们说了一下UWP应用中计算照片面积的玩法,而且老周也表示会提供WPF版本的示例。所以,今天就给大伙们补上吧。

    WPF是集成在.net框架中,属于.net的一部分,千万不要跟我说你学.net不学WPF,那是不对的,包括ASP.NET、WCF、WF等都是.net框架的一部分,它们在本质上并没有脱离.net。

    废话少扯,扯了也没人听,咱们说正题吧。

    WPF库中与UWP的不太一样,图像解码编码API似乎不像UWP中那么强大,大概是因为桌面程序可以调用Win32 API和COM的原因吧。不过,老周必须告诉你一个事,经过测试,用WPF的方法计算照片面积,在性能上远远超过GDI的方式,尤其是对大型照片更是如此。在WPF出现前,在System.Drawing命名空间下有个Iamge类,也派生出了一个Bitmap类,这些类都可以用来计算照片面积。老周在N年前做过一个照片面积计算器,除了计算面积外,还可以输入单价(每平方米多少钱),然后计算冲印照片的面积和最终的价格,并具有简单的票据打印功能。这个程序是基于GDI,即用System.Drawing命名空间下的东东搞的,因为那个时代,WPF还没有问世。

    WPF把图像的解码/编码类都内置到UI相关的媒体功能中,就是位于System.Windows.Media.Imaging命名空间下,里面有个BitmapImage类,它可以读到照片的像素宽高,以及分辨率,有了这些参数就可以计算面积了。

    但是,你必须注意WPF的线程安全模型是相对严格的,为了保护UI线程不被无意破坏,BitmapImage类是间接继承DispatcherObject类,凡是有Dispatcher的对象,你得明白,它是不能跨线程操作的。如果你要在UI上显示它们,那么位图对象的实例必须属于UI线程。

    如果你使用数据绑定时担心性能受影响,可以开启异步绑定,WPF的Binding有个IsAsync属性,开启它,UI线程在调度时会使用辅助线程来加载数据。实验表明,你的CPU核数越多,处理起来越快,到底还是要看配置啊。尤其是处理多媒体,像视频这些,你不能拿一台50年前的电脑来谈性能优化,你应该至少拿一台跟得上时代的电脑来评估。你总不能用春秋战国时期的社会生产力来跟大唐盛世比。

    在使用图像时,为了节省开销,你可以设置DecodePixelWidth或DecodePixelHeight

    属性,这两个属性不要同时设置,你设置其中一个就行了,这样图像在呈现时可以自动计算比例,不然会使图像变形,当然了,如果你希望图像变形,那就另当别论了。

    设置这两个属性,并不影响我们读取图像的真实像素,要获取图像宽高,应访问PixelWidth和PixelHeight属性,DecodePixelHeight/Width不会影响这两个属性的值。另外,通过DpiX和DpiY属性就能得到水平和垂直方向上的分辨率。

    好,同样地,还是先封装一个数据类。

        public class PhotoData : INotifyPropertyChanged
        {
            #region 私有字段
            int width = default(int), height = default(int);
            double dpix = default(double), dpiy = default(double);
            BitmapImage bitmap = null;
            double area;
            #endregion
    
            #region INotifyPropertyChanged成员
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
    
            #region 构造函数
            public PhotoData(string filePath)
            {
                // 实例化图像对象
                PhotoImage = new BitmapImage();
                PhotoImage.DecodePixelWidth = 100;
                // 初始化图像
                PhotoImage.BeginInit();
                PhotoImage.UriSource = new Uri(filePath);
                PhotoImage.EndInit();
    
                // 获取需要的参数
                Width = PhotoImage.PixelWidth;
                Height = PhotoImage.PixelHeight;
                DpiX = PhotoImage.DpiX;
                DpiY = PhotoImage.DpiY;
                // 计算面积
                ComputeArea();
            }
            #endregion
    
            #region 方法
            private void OnPropertyChanged([CallerMemberName]string prpname = "")
            {
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prpname));
            }
    
            /// <summary>
            /// 计算面积
            /// </summary>
            private void ComputeArea()
            {
                // 先将宽度和高度转为英寸
                double inchW = Width / DpiX;
                double inchH = Height / DpiY;
                /*
                    面积单位为平方米
                    1英寸 = 2.54厘米
                */
                Area = (inchW * 2.54d) * (inchH * 2.54d) / 10000d;
            }
            #endregion
    
            #region 属性
            /// <summary>
            /// 照片宽度
            /// </summary>
            public int Width
            {
                get { return width; }
                private set
                {
                    if (value != width)
                    {
                        width = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            /// <summary>
            /// 照片高度
            /// </summary>
            public int Height
            {
                get { return height; }
                private set
                {
                    if (value != height)
                    {
                        height = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            /// <summary>
            /// 水平分辨率
            /// </summary>
            public double DpiX
            {
                get { return dpix; }
                private set
                {
                    if (dpix != value)
                    {
                        dpix = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            /// <summary>
            /// 垂直分辨率
            /// </summary>
            public double DpiY
            {
                get { return dpiy; }
                private set
                {
                    if (value != dpiy)
                    {
                        dpiy = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            /// <summary>
            /// 图像实例
            /// </summary>
            public BitmapImage PhotoImage
            {
                get { return bitmap; }
                private set
                {
                    if (bitmap != value)
                    {
                        bitmap = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            /// <summary>
            /// 面积(平方米)
            /// </summary>
            public double Area
            {
                get { return area; }
                private set
                {
                    if (value != area)
                    {
                        area = value;
                        OnPropertyChanged();
                    }
                }
            }
            #endregion
        }

    这个我不多解释了,应该比昨天那个更好懂。注意我今天用的单位是平方米,计算平方厘米后,要除以10000。

    类似的方法,我们先计算单个照片的面积,并放入到一个集合中。

                    string[] files = openFileDlg.FileNames;
                    // 开始计算
                    photolist.Clear();
                    isRunning = true;
                    foreach (string f in files)
                    {
                        try
                        {
                            PhotoData data = new PhotoData(f);
                            photolist.Add(data);
                        }
                        catch (Exception ex)
                        {
                            // 记录异常信息
                            Trace.WriteLine($"{DateTime.Now.ToLongTimeString()} -- {ex.Message}");
                            continue;
                        }
                    }

    然后,统计总面积。

                    double totalArea = photolist.Sum(d =>
                    {
                        if (double.IsInfinity(d.Area))
                        {
                            return 0d;
                        }
                        return d.Area;
                    });

    为了使这个程序更加生动可爱,更具有内涵,老周还使用了语音朗读API,在System.Speech.Synthesis命名空间下。

                    // 语音朗读
                    Task.Run(() =>
                    {
                        using (SpeechSynthesizer symthedizer = new SpeechSynthesizer())
                        {
                            symthedizer.Speak(msg);
                        }
                    });

    使用Task来异读朗读,不影响UI上呈现内容。

    最后结果请看下图。

    好了,该开饭了,今天的牛皮就吹到这里,有空继续吹。

    示例源代码下载

  • 相关阅读:
    fiddler教程--拦截并篡改
    string
    希尔排序(缩小增量排序)
    k-选取问题
    完善版本散列表
    c++ BST继承自二叉树
    DEDECMS与DZ论坛如何整合,DEDE网站调用DZ内容
    DEDECMS如何实现定时发布文章
    DEDECMS如何设置图片上传加水印
    DEDE如何制作不同背景的列表
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5077863.html
Copyright © 2011-2022 走看看