计算照片的面积(UWP篇)

今天先说UWP应用程序上计算照片面积的方法,改天有空,再说说WPF篇。

其实计算照片面积的原理真TMD简单,只要你有本事读到照片的像素高度和宽度,以及水平/垂直方向上的分辨率(DPI)就可以了。计算方法也很容易,把像素值除以DPI,得到的是照片的宽度或高度,单位是英寸。

通常咱们计算面积是按平方米来算(不信你问问数码摄影店的伙计们),也可以按平方厘米来算。没关系,只要算出平方厘米,你就知道怎么转为平方米了。英寸和厘米的换算是:

1 inch = 2.54 cm

好,思想工作做完了,接下来就是开工。

首先,定义一个封装照片信息的类,UI和代码分离嘛,最好这样做,把要用的数据都进行封装,这样程序看起来也高大上,至少装逼是没问题的。

    public sealed class PhotoInfo : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }

        #region 私有字段
        // 宽、高,单位像素
        uint m_width = default(uint), m_height = default(uint);
        // 分辨率
        double m_dpix = default(double), m_dpiy = default(double);
        // 面积,单位为平方厘米
        double m_area = default(double);
        // 文件名
        string m_filename = null;
        #endregion

        #region    公共属性
        /// <summary>
        /// 照片宽度
        /// </summary>
        public uint Width
        {
            get { return m_width; }
            private set
            {
                if (m_width != value)
                {
                    m_width = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// 照片高度
        /// </summary>
        public uint Height
        {
            get { return m_height; }
            private set
            {
                if (value != m_height)
                {
                    m_height = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// 水平分辨率
        /// </summary>
        public double DpiX
        {
            get { return m_dpix; }
            private set
            {
                if (value != m_dpix)
                {
                    m_dpix = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// 垂直分辨率
        /// </summary>
        public double DpiY
        {
            get { return m_dpiy; }
            private set
            {
                if (m_dpiy != value)
                {
                    m_dpiy = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// 面积,平方厘米
        /// </summary>
        public double Area
        {
            get { return m_area; }
            private set
            {
                if (m_area != value)
                {
                    m_area = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName
        {
            get { return m_filename; }
            private set
            {
                if (m_filename != value)
                {
                    m_filename = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion

        #region 公共方法
        public async Task ProcessFileAsync(StorageFile file)
        {
            FileName = file.Name;
            using (IRandomAccessStream stream = await file.OpenReadAsync())
            {
                // 解码
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, stream);
                // 读取各个值
                Width = decoder.PixelWidth;
                Height = decoder.PixelHeight;
                DpiX = decoder.DpiX;
                DpiY = decoder.DpiY;
            }
            // 计算面积
            double w = Width / DpiX;  //英寸
            double h = Height / DpiY; //英寸
            // 1 inch = 2.54 cm
            Area = w * 2.54d * h * 2.54d;
        }
        #endregion
    }

这个类的代码有点长,不要紧,都是一堆属性,重点的是那个异步方法,从照片文件进行解码,然后读出图片大小、分辨率等信息。

                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, stream);
                // 读取各个值
                Width = decoder.PixelWidth;
                Height = decoder.PixelHeight;
                DpiX = decoder.DpiX;
                DpiY = decoder.DpiY;

老周相信你没有忘记BitmapDecoder这个东东,如果你忘了,请写一份3万字的检讨书,并提交到应用商店。解码后,从DpiX和DpiY两个属性就能读到分辨率;从PixelWidth和PixelHeight两个属性可以得到用像素表示的宽度和高度。

好,需要的数据都齐全了,然后计算图片的宽高的英寸表示值。

            double w = Width / DpiX;  //英寸
            double h = Height / DpiY; //英寸

最后,就可以计算单张照片的面积了,我这里用的单位是平方厘米。

            // 1 inch = 2.54 cm
            Area = w * 2.54d * h * 2.54d;

OK,这个封装的玩意儿算完成了,下面弄UI部分。大概的XAML如下,我就不解释了,看不懂的话,可以打电话问问憨豆先生。

        <Button Content="选择照片(可多选)"  Margin="10,15,10,8" Click="OnClick"/>

        <ListView Name="lv" Grid.Row="1" Margin="5">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock FontSize="18" FontWeight="Bold" Text="{Binding FileName}" />
                        <TextBlock>
                            <Run>尺寸(像素):</Run>
                            <Run Text="{Binding Width}" />
                            <Run> × </Run>
                            <Run Text="{Binding Height}" />
                        </TextBlock>
                        <TextBlock>
                            <Run>分辨率:</Run>
                            <Run Text="{Binding DpiX}"/>
                            <Run> × </Run>
                            <Run Text="{Binding DpiY}"/>
                        </TextBlock>
                        <TextBlock>
                            <Run>面积(平方厘米):</Run>
                            <Run Text="{Binding Area}"/>
                        </TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ListView>

        <TextBlock Grid.Row="2" Name="tbTotal" Margin="10,6" FontSize="20" Foreground="LightGreen" TextWrapping="Wrap" />

ListView用来显示每张照片的信息,最后的TextBlock用来显示所有照片的总面积。

然后,处理按钮事件。

            // 选择文件
            FileOpenPicker picker = new FileOpenPicker();
            picker.FileTypeFilter.Add(".jpg");
            picker.FileTypeFilter.Add(".jpeg");

            IReadOnlyList<StorageFile> imgfiles = await picker.PickMultipleFilesAsync();
            photolist.Clear();
            foreach (StorageFile f in imgfiles)
            {
                try
                {
                    PhotoInfo info = new PhotoInfo();
                    // 处理数据
                    await info.ProcessFileAsync(f);
                    // 添加到集合中
                    photolist.Add(info);
                }
                catch(Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }

            // 对面积进行合计
            double areaTotal = photolist.Sum(p =>
             {
                 // 如果单张照片的面积无效,则返回0
                 if (double.IsInfinity(p.Area))
                 {
                     return 0d;
                 }
                 return p.Area;
             });

            // 显示
            tbTotal.Text = $"共扫描了 {photolist.Count} 张照片,总面积为 {areaTotal.ToString("F2")} 平方厘米。";

photolist是个变量,类型为ObservableCollection<PhotoInfo>,ObservableCollection集合是个好东西,它可以自动更新绑定的集合控件的UI项。

由于在读取文件和解码图片时用到了异步等待,所以刚才在PhotoInfo类的定义时公开了一个ProcessFileAsync方法,这样确保调用这个类的代码也能继续异步等待,就是等到所有数据都读完了,都计算完了再继续,不然最后计算总面积的时候,因为数据没有准备好,会得到总面积为0的灵异结果。所以,做人别太急,要学会异步等待,这样你才能看看沿途美丽如画的风景。

所以,在向集合Add项前,要等待数据初始化完成,这个等待是异步的,不会阻止UI线程。

                    PhotoInfo info = new PhotoInfo();
                    // 处理数据
                    await info.ProcessFileAsync(f);
                    // 添加到集合中
                    photolist.Add(info);

最后计算总面积的时候,就好办了,直接调用集合的Sum扩展方法即可,你要是嫌代码太短了,也可以用LinQ来计算,反正一样的。注意,在Sum里面的Lambda表达式体中,要判断一下,每一个PhotoInfo实例的Area属性是否有效,方法是用double.IsInfinity方法,如果double值为正无穷大或负无穷大,就返回true,这时候应把返回的double值调整为0,不然的话,任何数跟无穷大的数相加后的结果,永远都是无穷大,这样的值没有实际价值。

            double areaTotal = photolist.Sum(p =>
             {
                 // 如果单张照片的面积无效,则返回0
                 if (double.IsInfinity(p.Area))
                 {
                     return 0d;
                 }
                 return p.Area;
             });

为什么要这样验证呢,因为个别照片文件可能由于人品问题,读不出正确的分辨率,这样最后的计算结果就会有问题。

现在,运行示例,然后选择一堆照片,就能计算它们的面积了,有图有真相。

好了,本文就写到这里了,改天老周再补上WPF篇。

示例源代码下载地址

时间: 2024-12-07 22:17:41

计算照片的面积(UWP篇)的相关文章

计算照片的面积(WPF篇)

昨天,老周突发其想地给大伙伴们说了一下UWP应用中计算照片面积的玩法,而且老周也表示会提供WPF版本的示例.所以,今天就给大伙们补上吧. WPF是集成在.net框架中,属于.net的一部分,千万不要跟我说你学.net不学WPF,那是不对的,包括ASP.NET.WCF.WF等都是.net框架的一部分,它们在本质上并没有脱离.net. 废话少扯,扯了也没人听,咱们说正题吧. WPF库中与UWP的不太一样,图像解码编码API似乎不像UWP中那么强大,大概是因为桌面程序可以调用Win32 API和COM

利用向量积(叉积)计算三角形的面积和多边形的面积

利用向量积(叉积)计算三角形的面积和多边形的面积: 向量的数量积和向量积: (1)  向量的数量积   (1)  向量的向量积 两个向量a和b的叉积(向量积)可以被定义为: 在这里θ表示两向量之间的角夹角(0° ≤ θ ≤ 180°),它位于这两个矢量 所定义的平面上. 向量积的模(长度)可以解释成以a和b为邻边的平行四边形的面积.求三角形ABC的面积,根据向量积的意义,得到: a=axi+ayj+azk; b=bxi+byj+bzk; a×b=(aybz-azby)i+(azbx-axbz)j

计算闭合区域面积

/// <summary> /// 计算闭合区域面积 /// </summary> /// <param name="X"></param> /// <param name="Y"></param> /// <param name="numPoints"></param> /// <returns></returns> doub

控制台输入计算园的面积

/**** * 控制台输入计算园的面积 * @author yanlong * 2017/4/30 */package java2;import java.util.*;public class liti2_11 {public static void main(String[] args){ Scanner input=new Scanner(System.in); System.out.println("请输入半径:"); double radius=input.nextDoubl

Java学习之路---计算圆形的面积和周长

题目:计算圆形的面积,其中圆形的半径是随意指定. 源代码以及所有的分析思路都如下: import java.util.Scanner;   //引入Scanner类 public class TestArea { /**  * @param args  */ public static void main(String[] args) { // TODO Auto-generated method stub /*大致思路分析  1. 首先要让用户输入圆形的半径,因为半径是随意指定的  2.运用圆

经典封装之计算三角形的面积

<!doctype html> <html> <head> <meta charset="utf-8"> <title>计算三角形的面积</title> <script> //创建一个计算三角形 function test(){ var h,d,s; h=5; d=8; s=h*d/2; alert(s); } test(); //活数据 function mj(h,j){ var s; s=h*j/2

由顶点坐标计算任意多边形面积

我们知道,如果三角形的一个顶点在原点,另两点A(x1 , y1)和B(x2 , y2) 则其面积可以表示为 SABC =0.5× |OA|×|OB|×sin(∠AOB) =0.5×|OA×OB| =0.5×|(x1,y1)×(x2,y2)| =0.5×[(x1y2)-(y1x2)] 以下图中的三角形ABC为例,欲求SABC 从原点,将ABC以向量形式表示 因此SABC = SOBC-SOAC-SOAB =SABC = SOBC + (-SOAC) + (-SOAB) 这样直接求ABC的面积转化为

【改革春风吹满地 HDU - 2036 】【计算几何-----利用叉积计算多边形的面积】

利用叉积计算多边形的面积 我们都知道计算三角形的面积时可以用两个邻边对应向量积(叉积)的绝对值的一半表示,那么同样,对于多边形,我们可以以多边形上的一个点为源点,作过该点并且过多边形其他点中的某一个的多条射线,这样就可以把该多边形变为多个三角形,然后利用叉积求面积即可. 不过要注意,对于三角形可以简单的用叉积的绝对值的一半表示,但对于多边形不可随意将它分割成的几个三角形对应的叉积的绝对值相加,要有一定顺序才可. 对于三角形,有 [该图片来源:https://www.cnblogs.com/xie

python计算圆的面积

引入pi的两种方法: 方法一: import math print(math.pi) 方法二: from math import pi print(pi) 计算圆的面积的代码: #计算圆的面积 from math import pi r=float(input('输入半径的长度:')) area=pi*r**2 print('输出圆的面积:',area) 原文地址:https://www.cnblogs.com/condom/p/12417036.html