【C#】计算多边形的面积

news2024/11/26 0:28:47

一、问题分析

在 C# 中计算多边形面积的一种常见方法是使用顶点坐标。

假设您有一个由一系列 (x, y) 顶点坐标定义的多边形,您可以使用“鞋带公式”(也称为高斯公式)来计算其面积。

如果是计算多边形的面积可以分为正常多边形、dicom图像中的多边形,这两种多边形可能考虑是否需要像素间距的转换问题。

 

二、考虑像素间距转换

在处理 DICOM 图像中的多边形面积计算时,如果需要更精确的结果,通常是需要考虑像素间距的。

像素间距在 DICOM 图像中用于描述每个像素在实际物理空间中的大小。如果不考虑像素间距,计算得到的面积只是基于像素坐标的数值,而不是真实的物理面积。

以下是一个示例,展示如何在计算多边形面积时考虑像素间距:

2.1 方法一

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 假设多边形的顶点坐标列表
        List<Point> points = new List<Point>
        {
            new Point(0, 0),
            new Point(1, 0),
            new Point(1, 1),
            new Point(0, 1)
        };

        // 假设像素间距
        double pixelSpacingX = 0.5; 
        double pixelSpacingY = 0.5;

        double area = CalculatePolygonAreaWithSpacing(points, pixelSpacingX, pixelSpacingY);
        Console.WriteLine($"考虑像素间距后的多边形面积为: {area}");
    }

    static double CalculatePolygonAreaWithSpacing(List<Point> points, double pixelSpacingX, double pixelSpacingY)
    {
        int count = points.Count;
        double area0 = 0;
        double area1 = 0;

        for (int i = 0; i < count; i++)
        {
            var x = points[i].X * pixelSpacingX;
            var y = (i + 1 < count? points[i + 1].Y : points[0].Y) * pixelSpacingY;
            area0 += x * y;

            var lat = points[i].Y * pixelSpacingY;
            var lon = (i + 1 < count? points[i + 1].X : points[0].X) * pixelSpacingX;
            area1 += x * y;
        }

        return Math.Round(Math.Abs(0.5 * (area0 - area1)), 2); 
    }

    class Point
    {
        public double X { get; set; }
        public double Y { get; set; }

        public Point(double x, double y)
        {
            X = x;
            Y = y;
        }
    }
}

2.2 方法二

public double CalculatePolygonArea()
{
    Tuple<double, double>[] tupleArray = new Tuple<double, double>[m_MaxPointNum];
    for (int i = 0; i < m_MaxPointNum; i++)
    {
        var point = m_Points[i];
        tupleArray[i] = Tuple.Create((double)point.X, (double)point.Y);
    }
    WhaleFC.Image.DicomImg dcm = (WhaleFC.Image.DicomImg)m_WndPara.GetDCM();
    double[] nArrPixelSpacing = dcm.GetPixelSpacing();
    double nPixelSpacingY = nArrPixelSpacing[0];
    double nPixelSpacingX = nArrPixelSpacing[1];
    double area = 0;
    for (int i = 0; i < m_MaxPointNum; i++)
    {
        double x1 = tupleArray[i].Item1 * nPixelSpacingX;
        double y1 = tupleArray[i].Item2 * nPixelSpacingY;
        double x2 = tupleArray[(i + 1) % m_MaxPointNum].Item1 * nPixelSpacingX;
        double y2 = tupleArray[(i + 1) % m_MaxPointNum].Item2 * nPixelSpacingY;
        area += (x1 * y2) - (x2 * y1);
    }
    return Math.Abs(area / 2.0);
}

三、不考虑像素间距转换

要计算 DICOM(Digital Imaging and Communications in Medicine,医学数字成像和通信)图像中多边形的面积,一种常见的方法是使用鞋带公式(Shoelace Formula)或称为高斯面积公式。以下是使用 C#实现计算多边形面积的示例代码: 

3.1 方法一

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 假设多边形的顶点坐标列表
        List<Point> points = new List<Point>
        {
            new Point(0, 0),
            new Point(1, 0),
            new Point(1, 1),
            new Point(0, 1)
        };

        double area = CalculatePolygonArea(points);
        Console.WriteLine($"多边形面积为: {area}");
    }

    static double CalculatePolygonArea(List<Point> points)
    {
        int count = points.Count;
        double area0 = 0;
        double area1 = 0;

        for (int i = 0; i < count; i++)
        {
            var x = points[i].X;
            var y = i + 1 < count? points[i + 1].Y : points[0].Y;
            area0 += x * y;

            var lat = points[i].Y;
            var lon = i + 1 < count? points[i + 1].X : points[0].X;
            area1 += x * y;
        }

        return Math.Round(Math.Abs(0.5 * (area0 - area1)), 2); 
    }

    class Point
    {
        public double X { get; set; }
        public double Y { get; set; }

        public Point(double x, double y)
        {
            X = x;
            Y = y;
        }
    }
}

3.2 方法二

//计算多边形面积
public double CalculatePolygonArea(Point[] points)
{
    double area = 0;
    int j = points.Length - 1;
    for (int i = 0; i < points.Length; i++)
    {
        area += (points[j].X + points[i].X) * (points[j].Y - points[i].Y);
        j = i;
    }
    return Math.Abs(area / 2.0);
}

四、注意点

4.1 多边形是闭合的

请注意,为了确保多边形闭合,最后一个顶点和第一个顶点应该是相同的。如果提供的顶点不形成闭合多边形,则该函数会自动处理这种情况,因为它通过取模操作将最后一个顶点与第一个顶点关联起来。

4.2 多边形的重叠部分需要注意 

多边形的重叠部分是否重复计算???

如果一个多边形的边相互交叉,那么按照鞋带公式计算得到的面积可能不会反映实际的几何形状,因为该公式假设多边形是简单且非自相交的。对于自相交多边形(即星形多边形),简单的鞋带公式可能无法正确计算面积,因为它可能会重复计算重叠区域。

为了正确处理自相交多边形的情况,一种方法是将其分解成多个不相交的简单多边形(通常是三角形),然后分别计算这些简单多边形的面积并进行求和。这通常涉及到更复杂的算法,例如三角剖分或者扫描线算法。

下面是一个简化的示例,使用一种方法来近似处理自相交多边形。这种方法基于对多边形边的处理,尝试确定每条边对面积的贡献。如果一条边跨越了另一条边,则需要调整计算逻辑以避免重叠区域被重复计算。但是请注意,这并不是一个完美的解决方案,对于复杂的自相交多边形,这种方法可能不会总是给出正确的结果。

以下是一个简化的处理自相交多边形的方法:

using System;
using System.Collections.Generic;

public class PolygonAreaCalculator
{
    public static double CalculatePolygonArea(List<Point> points)
    {
        if (points.Count < 3)
            throw new ArgumentException("There must be at least three points to define a polygon.");

        // Add the first point at the end to close the polygon.
        points.Add(points[0]);

        double area = 0.0;
        int n = points.Count;

        for (int i = 0; i < n - 1; i++)
        {
            Point p1 = points[i];
            Point p2 = points[i + 1];

            // Calculate the area contribution of each edge.
            area += (p1.X * p2.Y - p2.X * p1.Y);

            // Check if this edge crosses any other edges.
            for (int j = i + 2; j < n - 1; j++)
            {
                Point p3 = points[j];
                Point p4 = points[j + 1];

                // Check if the line segments (p1, p2) and (p3, p4) intersect.
                if (SegmentsIntersect(p1, p2, p3, p4))
                {
                    // Adjust the area calculation based on the intersection.
                    // This part is simplified and may not handle all cases correctly.
                    // A more sophisticated algorithm would be needed for complex cases.
                    area -= (p1.X * p2.Y - p2.X * p1.Y);
                }
            }
        }

        // Take the absolute value and divide by 2 to get the final area.
        return Math.Abs(area) / 2.0;
    }

    private static bool SegmentsIntersect(Point p1, Point p2, Point p3, Point p4)
    {
        // Implement a simple line segment intersection check.
        // This is a simplified version that does not handle all edge cases.
        double d1 = Direction(p3, p4, p1) * Direction(p3, p4, p2);
        double d2 = Direction(p1, p2, p3) * Direction(p1, p2, p4);

        return (d1 <= 0) && (d2 <= 0);
    }

    private static double Direction(Point p1, Point p2, Point p3)
    {
        return (p2.Y - p1.Y) * (p3.X - p2.X) - (p2.X - p1.X) * (p3.Y - p2.Y);
    }
}

public struct Point
{
    public double X;
    public double Y;

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }
}

这个方法尝试检查每条边是否与其他边相交,并根据交点调整面积计算。然而,这仍然是一种简化的处理方式,对于复杂的自相交多边形,需要更高级的算法来正确地计算面积。

如果您确实需要处理复杂的自相交多边形,并希望获得准确的结果,您可能需要考虑使用专门的几何处理库,如 CGAL (Computational Geometry Algorithms Library) 或者类似的库。

4.3 获取GDI+获取两个多边形区域相交、非相交区域

【C#】 使用GDI+获取两个多边形区域相交、非相交区域-CSDN博客文章浏览阅读227次。【C#】 使用GDI+获取两个多边形区域相交、非相交区域https://blog.csdn.net/wangnaisheng/article/details/140527315

4.4 在一个给定的宽、高范围内,获取到该多边形内部的所有坐标集合?

【C#】在一个给定的宽、高范围内,获取到该多边形内部的所有坐标集合?-CSDN博客文章浏览阅读594次,点赞9次,收藏7次。【C#】在一个给定的宽、高范围内,获取到该多边形内部的所有坐标集合?https://blog.csdn.net/wangnaisheng/article/details/140513467

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1985919.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

工单触发器如何助力企业提升效率?天润融通案例解析

工单&#xff0c;用于记录、跟踪和管理特定工作任务或服务请求的工具。通过工单可以完成任务跨组织、跨部门流转和协同等问题&#xff0c;例如&#xff1a; 市场线索→创建工单→判定需求→分配到区域→分配到销售→销售跟进→成功交易客户投诉建议→创建工单→分配到区域→分…

C语言入门基础题:传递闭包

1.题目描述 给定一张点数为 n 的有向图的邻接矩阵&#xff0c;图中不包含自环&#xff0c;求该有向图的传递闭包。张图的邻接矩阵定义为一个n xn 的矩阵 A (aij)nx”&#xff0c;其中(1,i到j存在直接连边aii0,i到j没有直接连边张图的传递闭包定义为一个„ xn的矩阵 B (bij)nx”…

职场中,真正的大人物都具有这几个特点

职位是组织赋予的&#xff0c;是领导提拔的&#xff0c;有些人失去了身上的标签、职位&#xff0c;很快就是失去往日的荣光。 职场中真正的牛人&#xff0c;他们不一定有多高的职位&#xff0c;但他们在领导眼里有分量&#xff0c;又能得到同事的尊重。 这类人身上最大的特征…

elasticsearch的学习(四):elasticsearch的一些基本概念

简介 elasticsearch的一些基本概念。 核心概念 索引&#xff1a;一个拥有相似特征的文档的集合。 类型&#xff1a;在索引中定义&#xff0c;是索引的一个逻辑上的分类&#xff0c;版本7以上已经弃用了。 文档&#xff1a;可被索引的基础信息单元&#xff0c;即一条数据&a…

【Android Studio】Intent实现两个Activity页面的跳转(基于Empty View Activity)

文章目录 为什么使用EmptyViewActivity而不是EmptyActivity准备工作&#xff1a;创建EmptyViewActivity项目准备工作&#xff1a;新建SecondActivity调试一下原始项目添加button写跳转方法测试布局技巧 为什么使用EmptyViewActivity而不是EmptyActivity 因为笔者使用的Android…

基于STM32的环境监测系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码传感器读取代码应用场景 家居环境监测工业环境监测常见问题及解决方案 常见问题解决方案结论 1. 引言 环境监测系统在我们的日常生活和工作中变得越来越重要。通过监测空气质量、…

LVS部署DR集群

介绍 DR&#xff08;Direct Routing&#xff09;&#xff1a;直接路由&#xff0c;是LVS默认的模式&#xff0c;应用最广泛. 通过为请求报文重新封装一个MAC首部进行转发&#xff0c;源MAC是DIP所在的接口的MAC&#xff0c;目标MAC是某挑选出的RS的RIP所在接口的MAC地址. 整个…

主从复制原理及配置

角色不生效&#xff1a; 在配置文件中添加 activate_all_roles_on_loginon glibc安装&#xff0c;my.cnf在项目目录之下 rpm安装&#xff0c;my.cnf文件在/etc/my.cnf 主从复制 备份的三种类型&#xff1a; 热备份 物理备份 逻辑备份 HA&#xff1a;高可用集群 复制原理…

实现Kubernetes中的抢占式Pod与固定Pod的弹性伸缩:一项全面指南

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

c++STL中list介绍,模拟实现和list与vector对比

目录 前言 &#xff1a; 1. list的介绍及使用 1.1list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list的迭代器失效 2. list的模拟实现 3. list与vector的对…

GuLi商城-商品服务-API-新增商品-调用远程服务保存优惠等信息

优惠券服务要开启服务注册和发现功能: 会员服务要开启远程调用功能: 可以写在消费端: 我还是习惯写在服务提供者一端: package com.nanjing.gulimall.coupon.feign;import com.nanjing.common.to.SpuBoundTo; import com.nanjing.common.utils.R; import org.springframewo…

Docker安装portainer汉化版

1、拉取汉化版Portainer镜像 docker pull 6053537/portainer-ce 2、创建数据卷 为了保存Portainer的数据和配置&#xff0c;应该创建一个Docker卷&#xff1a; docker volume create portainer_data 3、运行Portainer容器 docker run -d \--name portainer \-p 9000:9000…

企业社会责任(CSR)国际标准对企业的发展有什么好处?

企业社会责任&#xff08;CSR&#xff09;国际标准对企业的发展具有多方面的好处&#xff0c;包括但不限于以下几点&#xff1a; 提升企业声誉和形象 ​符合国际标准展示了企业对社会和环境的积极关注&#xff0c;有助于在消费者、投资者、合作伙伴和公众中树立良好的声誉&…

MyBatis快速学习

目录 前言 MyBatis的具体使用 一些小工具&#xff1a;MyBatisX 常见问题&#xff1a; 1.表中字段名和实体属性名不一致 2.按条件查询&#xff08;单条件&#xff09;时的&#xff0c;查询条件怎么编写 3.按条件查询&#xff08;多条件&#xff09; 4.多条件查询时&…

需要增重的小猫看过来!猫咪很瘦吃不胖是怎么回事?增重猫罐头推荐!

前几天医院来了只小母猫&#xff0c;7个月了&#xff0c;现在刚5斤。主人反映她平时干饭积极&#xff0c;能吃能睡&#xff0c;能喝能拉&#xff0c;就是不长肉。虽然大家都说母猫确实没有公猫容易胖&#xff0c;但是这只猫咪确实也长得也太慢了。其实我们医院接到过不少这样的…

对于springboot无法连接redis解决方案

对于springboot无法连接redis解决方案 一、测试是否能在本地应用上访问到你的redis&#xff08;如果是部署在linux上的话&#xff09;1. 开启telnet功能2. 开始测试端口是否能访问到&#xff08;适用于所有&#xff0c;包括MQ&#xff09;3. 开放6379端口4. 看spring的配置文件…

CANdela/Diva系列5--CANdela Studio的Data Types功能介绍

Data Types在CDD文件中是一个很重要的部分&#xff0c;它能够便捷地解析DID/RID的数据内容和0x19服务的相关子服务&#xff0c;同时能够能够全局定义一些变量&#xff0c;达到整个CDD文件公用的效果。 目录 1. Raw Value: 2. Text Table: 3. Linear: 4. Characteristic Cu…

生活需要BGM,悠律凝声环开放式耳机全场景通用

如今&#xff0c;BGM围绕着我们的生活&#xff0c;音乐是生活的调料品&#xff0c;深受运动爱好者的喜爱&#xff0c;不但能够缓解锻炼时的单调&#xff0c;也能够更好地激发我们的身体状态。最近我入手的悠律凝声环ringbuds pro就是这样一款特别适合运动场景使用。 开放式耳机…

go语言day22 gin-vue-admin全栈项目的依赖安装

flipped-aurora/gin-vue-admin: &#x1f680;ViteVue3Gin的开发基础平台&#xff0c;支持TS和JS混用。它集成了JWT鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器【可AI辅助】、表单生成器和可配置的导入导出等开发必备功能…

从零开始学习性能测试

学习目标 理解性能测试定义、目的理解常见性能测试策略理解性能指标理解性能测试方法学习性能测试工具 什么是性能测试 测试中的非功能测试其实范围比较广&#xff0c;性能、稳定性、安全性等都可以放进这个范畴。非功能测试&#xff0c;一般比功能测试门槛高些&#xff0c;多数…