今天先说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篇。

示例源代码下载地址

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

  1. 计算照片的面积(WPF篇)

    昨天,老周突发其想地给大伙伴们说了一下UWP应用中计算照片面积的玩法,而且老周也表示会提供WPF版本的示例.所以,今天就给大伙们补上吧. WPF是集成在.net框架中,属于.net的一部分,千万不要跟 ...

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

    题目:计算圆形的面积,其中圆形的半径是随意指定. 源代码以及所有的分析思路都如下: import java.util.Scanner; //引入Scanner类 public class TestAr ...

  3. Java计算几何图形的面积

    对于每个几何图形而言,都有一些共同的属性,如名字.面积等,而其计算面积的方法却各不相同.为了简化开发,请编写程序,定义一个超类来实现输入名字的方法,并使用抽象方法来计算面积. 思路分析: 所谓超类就是 ...

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

    利用叉积计算多边形的面积 我们都知道计算三角形的面积时可以用两个邻边对应向量积(叉积)的绝对值的一半表示,那么同样,对于多边形,我们可以以多边形上的一个点为源点,作过该点并且过多边形其他点中的某一个的 ...

  5. 用java编写一个函数,用于计算桌子的面积,可计算任意边长的桌子

    /* *桌子实体类,有属性和方法 */public class Table {    String name; // 声明桌子名称    Double width; // 声明桌子宽度    Doub ...

  6. Java入门:基础算法之计算园的面积

    本部分内容介绍如何使用Java计算圆的周长和面积.分两种方法来实现: 1)圆的半径由用户输入 2)圆的半径由程序指定 代码1: /** * @作者: 理工云课堂 * @描述: 用户输入圆的半径,程序结 ...

  7. Android GIS开发系列-- 入门季(7) 利用GeometryEngine坐标转换、计算距离与面积等

    GeometryEngine是Arcgis的重要工具类,利用此工具类,可以计算地图上的距离.面积,将点.线.面转化为Json数据,将Json转化为点线面,坐标转换作用非常强大. 一.坐标转化 将用到方 ...

  8. C++编程计算图形的面积(圆、矩形)

    C++基础,while循环与if判断实现的计算图形面积 1 #include <iostream> 2 3 int main() { 4 while (true){ 5 int input ...

  9. Javascript运用函数计算正方形的面积

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

随机推荐

  1. 关于开启.NET在线提升教育培训的通知! - 可在此页面观看在线直播!

    年前在线公开课程通知: 近期在开启VIP课程,隔天讲一次,年前其它时间插空讲公开课,主题:设计模式系列 1:培训 - 大概不会讲的内容: 1:不讲系列. 2:不讲入门. 3:不讲我不懂的! 2:培训 ...

  2. 匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密

    0x00 前言 在匹夫的上一篇文章<匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置>的最后,匹夫以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够过瘾,很多需要说清楚 ...

  3. Ubuntu 16.10 安装KolourPaint 4画图工具

    KolourPaint 4画图工具简单实用,可以绘画.视频处理和图标编辑: • 绘画:绘制图表和“手绘” • 视频处理:编辑截图和照片;应用特效 • 图标编辑:绘画剪贴和标识透明化 1.在Ubuntu ...

  4. C#异步编程(二)

    async和await结构 序 前篇博客异步编程系列(一) 已经介绍了何谓异步编程,这篇主要介绍怎么实现异步编程,主要通过C#5.0引入的async/await来实现. BeginInvoke和End ...

  5. [原][Docker]特性与原理解析

    Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...

  6. [转载]C#中MessageBox.Show用法以及VB.NET中MsgBox用法

    一.C#中MessageBox.Show用法 MessageBox.Show (String) 显示具有指定文本的消息框. 由 .NET Compact Framework 支持. MessageBo ...

  7. WebApi基于Token和签名的验证

    最近一段时间在学习WebApi,涉及到验证部分的一些知识觉得自己并不是太懂,所以来博客园看了几篇博文,发现一篇讲的特别好的,读了几遍茅塞顿开(都闪开,我要装逼了),刚开始读有些地方不理解,所以想了很久 ...

  8. css知多少之绝对定位小记

    一.position定位常见属性 对于属性position来说,属性值有static/relative/absolute/fixed/inherit以下只对绝对定位position:absolute详 ...

  9. (转) 将ASP.NET Core应用程序部署至生产环境中(CentOS7)

    原文链接: http://www.cnblogs.com/ants/p/5732337.html 阅读目录 环境说明 准备你的ASP.NET Core应用程序 安装CentOS7 安装.NET Cor ...

  10. Java集合类--温习笔记

    最近面试发现自己的知识框架有好多问题.明明脑子里知道这个知识点,流程原理也都明白,可就是说不好,不知道是自己表达技能没点,还是确实是自己基础有问题.不管了,再巩固下基础知识总是没错的,反正最近空闲时间 ...