golang 计算多边形的相似度

参考https://blog.csdn.net/qq_42391248/article/details/123892301
根据java版本写了个golang的版本

package main

import (
	"fmt"
	"math"
)

func main() {
	p1 := new(Polygon)
	p2 := new(Polygon)

	vec1 := new(Vector2)
	vec1.Vector2Init(0, 0)
	p1.AddVertex(*vec1)
	vec1.Vector2Init(100, 0)
	p1.AddVertex(*vec1)
	vec1.Vector2Init(100, 50)
	p1.AddVertex(*vec1)
	vec1.Vector2Init(75, 50)
	p1.AddVertex(*vec1)
	//
	vec2 := new(Vector2)
	vec2.Vector2Init(0, 0)
	p2.AddVertex(*vec2)
	vec2.Vector2Init(100, 0)
	p2.AddVertex(*vec2)
	vec2.Vector2Init(100, 50)
	p2.AddVertex(*vec2)
	vec2.Vector2Init(75, 50)
	p2.AddVertex(*vec2)
	// 传入两个多边形
	m := new(Matcher)
	m.MatcherInit(*p1, *p2)
	// 计算两个多边形的匹配率
	matchRate := m.Distance()
	fmt.Println("相似度:", 1-matchRate)
}

type TurningFunction struct {
	Range     []float64 //图形的x轴--边长变化
	Image     []float64 //图形y轴--角度变化
	RANGE_MAX float64
	EPSILON   float64
}
type Vector2 struct {
	X float64
	Y float64
}
type Polygon struct {
	Vertices    []Vector2
	Edges       []Vector2
	Perimeter   float64
	EdgeCount   int
	VertexCount int
}
type AlignedTF struct {
	SharedRangeEvents []float64
	EPSILON           float64
	TurningFunctionA  TurningFunction
	TurningFunctionB  TurningFunction
}
type Matcher struct {
	TurningFunctionA         TurningFunction
	ReshapedTurningFunctionB []TurningFunction
}

func (tf *TurningFunction) TurningFunctionInit() {
	tf.Range = make([]float64, 0)
	tf.Image = make([]float64, 0)
}
func (tf *TurningFunction) TurningFunctionInitWithPy(poly Polygon) {
	tf.Range = tf.CalculateNormalizedRange(poly)
	tf.Image = tf.CalculateImage(poly)
}

func (tf *TurningFunction) TurningFunctionInitWithTF(tfs TurningFunction) {
	tf.Range = tfs.Range
	tf.Image = tfs.Image
}

//CalculateImage 计算角度变化
func (tf *TurningFunction) CalculateImage(poly Polygon) []float64 {
	image := make([]float64, 0)
	edges := poly.Edges
	var angleAccumulator float64 = 0
	image = append(image, angleAccumulator)
	for i := 0; i < len(edges); i++ {
		edgeA := edges[i]
		edgeB := edges[(i+1)%len(edges)]

		var angle float64
		angle = edgeA.Dot(edgeB)

		lenA := edgeA.GetLength()
		lenB := edgeB.GetLength()

		angle /= lenA * lenB
		angle = math.Acos(angle)

		angleAccumulator += edgeA.CrossDirection(edgeB) * angle
		image = append(image, angleAccumulator)
	}
	return image
}

//ValueAt 二分查找
func (tf *TurningFunction) ValueAt(x float64) float64 {
	tf.EPSILON = 1e-10
	low := 0
	high := len(tf.Range)
	for low <= high {
		mid := (low + high) / 2
		if math.Abs(tf.Range[mid]-x) < tf.EPSILON {
			return tf.Image[mid]
		}
		if tf.Range[mid] < x {
			if mid+1 < len(tf.Range) {
				if tf.Range[mid+1] > x {
					return tf.Image[mid]
				}
			}
			low = mid + 1
		} else if tf.Range[mid] > x {
			if mid-1 >= 0 {
				if tf.Range[mid-1] < x {
					return tf.Image[mid-1]
				}
			}
			high = mid - 1
		}
	}
	return -1
}

//CalculateNormalizedRange 计算边长变化
func (tf *TurningFunction) CalculateNormalizedRange(poly Polygon) []float64 {
	var ranges = make([]float64, 0)
	tf.RANGE_MAX = 1
	polygonPerimeter := poly.Perimeter
	edges := poly.Edges
	var perimeterAccumulator float64 = 0
	for i := 0; i < len(edges); i++ {
		segmentPerimeter := edges[i].GetLength()
		currentSegment := segmentPerimeter / polygonPerimeter
		perimeterAccumulator += currentSegment * tf.RANGE_MAX
		ranges = append(ranges, perimeterAccumulator)
	}
	return ranges
}

func (v2 *Vector2) Vector2Init(x, y float64) {
	v2.X = x
	v2.Y = y

}
func (v2 *Vector2) GetLength() float64 {
	return math.Sqrt(v2.X*v2.X + v2.Y*v2.Y)
}

func (v2 *Vector2) CrossDirection(vec Vector2) float64 {
	zCross := v2.X*vec.X - v2.Y*vec.Y
	if zCross > 0 {
		return 1.0
	} else if zCross < 0 {
		return -1.0
	} else {
		return 0.0
	}
}

//Dot 向量代数函数
func (v2 Vector2) Dot(vec Vector2) float64 {
	return v2.X*vec.X + v2.Y*vec.Y
}

func (v2 *Vector2) ToString() string {
	return fmt.Sprintf("( %f, %f )", v2.X, v2.Y)
}

//初始化,类似构造函数

func (p *Polygon) PolygonInit() {
	p.Vertices = make([]Vector2, 0)
	p.Edges = make([]Vector2, 0)
}
func (p *Polygon) PolygonInitWithParam(vector2Lst []Vector2) {
	p.Vertices = vector2Lst
	p.Edges = make([]Vector2, 0)
	for _, item := range vector2Lst {
		p.Perimeter += item.GetLength()
	}
}

/// <summary>
/// 添加顶点
/// </summary>
/// <param name="vector2"></param>
func (p *Polygon) AddVertex(vector2 Vector2) {
	var lastVertex Vector2

	p.Vertices = append(p.Vertices, vector2)
	p.VertexCount++

	if len(p.Vertices) == 1 {
		return
	} else {
		lastVertex = p.Vertices[len(p.Vertices)-2]
	}

	vec := new(Vector2)
	vec.Vector2Init(vector2.X-lastVertex.X, vector2.Y-lastVertex.Y)
	p.AddEdge(*vec)
	p.EdgeCount++
}

/// <summary>
/// 添加边
func (p *Polygon) AddEdge(edge Vector2) {
	p.Edges = append(p.Edges, edge)
	p.Perimeter += edge.GetLength()
}

func (p *Polygon) ToString() string {
	return fmt.Sprintf("边数:%d ,顶点数:%d ,周长:%f", p.EdgeCount, p.VertexCount, p.Perimeter)
}

func (atf *AlignedTF) AlignedTFInitWithParam(tfA, tfB TurningFunction) {
	atf.EPSILON = 1e-10
	atf.TurningFunctionA = tfA
	atf.TurningFunctionB = tfB
	rangeA := tfA.Range
	rangeB := tfB.Range
	atf.SharedRangeEvents = make([]float64, 0)
	iterA := 0
	iterB := 0
	// assuming both ranges start at 0
	atf.SharedRangeEvents = append(atf.SharedRangeEvents, rangeA[iterA])
	iterA++
	for iterA != len(rangeA) {
		// *iterA == *iterB
		if rangeB[iterB]-rangeA[iterA] > atf.EPSILON {
			atf.SharedRangeEvents = append(atf.SharedRangeEvents, rangeA[iterA])
			if iterA != len(rangeA) {
				iterA++
				continue
			}
		}
		// *iterA > *iterB
		if rangeA[iterA]-rangeB[iterB] > atf.EPSILON {
			atf.SharedRangeEvents = append(atf.SharedRangeEvents, rangeB[iterB])
			if iterB != len(rangeB) {
				iterB++
				continue
			}
		}
		// *iterA < *iterB
		if rangeA[iterA]-rangeB[iterB] < atf.EPSILON {
			atf.SharedRangeEvents = append(atf.SharedRangeEvents, rangeA[iterA])
			iterA++
			iterB++
			continue
		}
	}

	for iterB != len(rangeB) {
		atf.SharedRangeEvents = append(atf.SharedRangeEvents, rangeB[iterB])
	}
}

func (atf *AlignedTF) Distance() float64 {
	var dis float64 = 0
	for i := 0; i < len(atf.SharedRangeEvents)-1; i++ {
		currentX := atf.SharedRangeEvents[i]
		imageA := atf.TurningFunctionA.ValueAt(currentX)
		imageB := atf.TurningFunctionB.ValueAt(currentX)

		nextX := atf.SharedRangeEvents[i+1]

		dis += (nextX - currentX) * (imageB - imageA) * (imageB - imageA)
	}

	dis = math.Sqrt(dis)
	return dis
}

/** polyA 匹配库中多边形
    polyB 待匹配多边形
**/
func (m *Matcher) MatcherInit(polyA, polyB Polygon) {
	tf := new(TurningFunction)
	tf.TurningFunctionInitWithPy(polyA)
	m.TurningFunctionA = *tf
	m.ReshapedTurningFunctionB = m.GenerateReshapedFunction(polyB)
}

// 更改多边形的顶点顺序
// 将index作为首个顶点,构造多边形
func (m *Matcher) GenerateReshapedPolygon(poly Polygon, startIndex int) Polygon {
	reshapedPoly := new(Polygon)

	for vertex := startIndex; vertex < len(poly.Vertices); vertex++ {
		reshapedPoly.AddVertex(poly.Vertices[vertex])
	}

	for vertex := 0; vertex < startIndex; vertex++ {
		reshapedPoly.AddVertex(poly.Vertices[vertex])
	}

	return *reshapedPoly
}

// 计算多边形的所有tf
func (m *Matcher) GenerateReshapedFunction(poly Polygon) []TurningFunction {
	numReshapedFuncs := poly.EdgeCount
	reshapedFunc := make([]TurningFunction, 0)
	for i := 0; i < numReshapedFuncs; i++ {
		startIndex := i
		reshapedPoly := m.GenerateReshapedPolygon(poly, startIndex)
		tf := new(TurningFunction)
		tf.TurningFunctionInitWithPy(reshapedPoly) //
		reshapedFunc = append(reshapedFunc, *tf)
	}

	return reshapedFunc
}

func (m *Matcher) Distance() float64 {
	var minDistance = float64(int(^uint(0) >> 1))
	for i := 0; i < len(m.ReshapedTurningFunctionB); i++ {
		atf := new(AlignedTF)
		atf.AlignedTFInitWithParam(m.TurningFunctionA, m.ReshapedTurningFunctionB[i])
		dis := atf.Distance()
		if dis < minDistance {
			minDistance = dis
		}
	}
	return minDistance
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值