减少曲线上的象素数目的算法?

iamredeye 2004-07-28 11:35:32
大概是这样的:我有一条连续的闭合曲线,每个点是一个象素,这条曲线上一共有大概几百到几千个点,我需要最多保留其中200个点(更少也可),要求这些保留下来的点尽可能描绘出原来的形状,也就是说曲率大的地方就多保留一些点,较平坦的地方就少保留一些点,直线则可以完全省略中间各点。我有每两个点之间的方向值,从0到7共8个方向(其实就是Freeman链码),那么每个点的弯曲程度可以用前后方向的差的绝对值来表示,最大差为7,最小为0,为0的地方就是方向不变的点,可以直接去掉,为7的点则最需要保留,因为它可能对图象的形状影响最大(另:需要的话是不是也可以求2阶的差呢?有点类似于二阶导数)。
接下来就按每个点的弯曲程度删除其中不那么重要的点了,统计出方向变化为0的点的个数C0,方向变化为1的点的个数C1,...,方向变化为7的点的个数C7,假设未简化前的点共T个,这样问题就变成C0+C1+C2+...+C7=T,C0*X0 + C1*X1 + C2*X2 + ... + C7*X7 < =200,其中Xi为Ci应该保留下来的比例系数,(X0肯定为0,它是直线中间的点),我现在不知道怎样确定Xi才最合理并有理论根据,如何让每个Xi都和所有其他参数有关呢?就是说计算X7的时候要考虑到T和200以及其他Ci的关系?随便找几个Xi肯定是可以做到的,但是缺乏理论根据。而且坦率讲,我希望能在数学上稍微复杂一点:)
...全文
158 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
dxhdxh2k 2005-04-14
  • 打赏
  • 举报
回复
mark
iamredeye 2004-08-10
  • 打赏
  • 举报
回复
我进“管理”了, 还是没找到怎么结贴啊,天呐,我怎么这么蠢呢?(还是csdn做的过于复杂了?)
iamredeye 2004-08-10
  • 打赏
  • 举报
回复
多谢各位,尤其是IT_Fly,帮我解决了问题
zzwu 2004-08-06
  • 打赏
  • 举报
回复
能弄清问题,并感到思路简单,就行了.

结贴的方法,就是点击上面的菜单条:

" 回复 | 推荐 | 收藏 | 专题 | 公告 | 管理 | 保存 | 关闭窗口 "

中的" 管理", 进入后就可以结贴了.
iamredeye 2004-08-05
  • 打赏
  • 举报
回复
哇,最近陆续发现了好多这种算法,以前我怎么不知道呢,看了以后总在想:“思路这么简单,其实我想想也能想出来啊”,看来还是第一个吃螃蟹的人厉害啊,走别人走过的路太容易多了:)
iamredeye 2004-08-01
  • 打赏
  • 举报
回复
哇,怎么感谢IT_Fly老兄呢!真不知说什么好!我觉得csdn上真的是很多热心人,我是新人,不过以后会常来看看的。
另:怎么给分呢?哪位能教教我,没看到给分的地方啊,查帮助也没找到
iamredeye 2004-08-01
  • 打赏
  • 举报
回复
怎么给分呀?不会啊
iamredeye 2004-08-01
  • 打赏
  • 举报
回复
我已经一个星期困在这个问题上了,一点眉目也没有,要挨批了:(
IT_Fly兄,能否给我email一些GIS上的采样算法和什么道格拉斯算法的资料啊,好的网址也可,感激不尽呐,我的email是iamredeye@sina.com,期待...
iamredeye 2004-08-01
  • 打赏
  • 举报
回复
IT_Fly:能不能给点这方面的资料啊,你说的我都没听说过,惭愧啊,另外:不考虑非常复杂的不规则曲线
梧桐168 2004-08-01
  • 打赏
  • 举报
回复
void CScribbleView::OnMouseMove(UINT nFlags, CPoint point)
{
m_cTool.OnMouseMove(point, *this);
}

void CScribbleView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_cTool.OnLButtonDown(point, *this);
}

void CScribbleView::OnLButtonUp(UINT nFlags, CPoint point)
{
m_cTool.OnLButtonUp(point, *this);
}
梧桐168 2004-08-01
  • 打赏
  • 举报
回复
#include "StdAfx.h"
#include "ScribbleTool.h"
#include "ScribbleView.h"
#include "GDIX.h"
#include "MathHelper.h"
#include "Math.h"

const double PI = 3.14159;

void CScribbleTool::OnMouseMove(const CPoint& pt, CView& cView)
{
if (m_bScribbling)
{
m_cVertexs.push_back(pt);
CClientDC dc(&cView);
CSelPen cPen1(&dc, RGB(0,0,255));
dc.Polyline(&m_cVertexs[0], static_cast<int>(m_cVertexs.size()));
}
}

void CScribbleTool::OnLButtonDown(const CPoint& pt, CView& cView)
{
if (!m_bScribbling)
{
m_bScribbling = TRUE;
m_cVertexs.clear();
cView.Invalidate();
}
}

void CScribbleTool::OnLButtonUp(const CPoint& pt, CView& cView)
{
if (m_bScribbling)
{
// 对鼠标经过的节点进行采样
Vertexs result_ppts;
DWORD start_time = ::GetTickCount();
Sampling(m_cVertexs, &result_ppts);
DWORD use_time = ::GetTickCount() - start_time;

// 绘制出采样后的折线
CClientDC dc(&cView);
CSelPen cPen1(&dc, RGB(255,0,0));
dc.Polyline(&result_ppts[0], static_cast<int>(result_ppts.size()));
// 绘制出采样后的节点
CSelPen cPen2(&dc, RGB(255,0,0));
CSelBrush cBrush(&dc, RGB(255,0,0));
for (size_t s = 0; s < result_ppts.size(); s++)
dc.Ellipse(result_ppts[s].x-2, result_ppts[s].y-2, result_ppts[s].x+2, result_ppts[s].y+2);
// 显示采样具体信息
string sampling_arithmetic;
switch(m_lSamplingArithmetic)
{
default:
ASSERT(FALSE);
break;
case Arithmetic_Douglas_Peuker:
sampling_arithmetic = "Douglas-Peuker";
break;
case Arithmetic_McMaster:
sampling_arithmetic = "McMaster";
break;
}
ostringstream format;
format << "采样算法:" << sampling_arithmetic << endl
<< "采样之前的点数:" << static_cast<int>(m_cVertexs.size()) << endl
<< "采样之后的点数:" << static_cast<int>(result_ppts.size()) << endl
<< "花费时间:" << use_time << "毫秒";
AfxMessageBox(format.str().c_str());

m_bScribbling = FALSE;
}
}

void CScribbleTool::Sampling(const Vertexs& sampling_pts, VertexsPtr result_ppts)
{
ASSERT(result_ppts);
result_ppts->clear();

if (sampling_pts.size() < 3)
{
result_ppts->assign(sampling_pts.begin(), sampling_pts.end());
}
else
{
result_ppts->push_back(sampling_pts[0]);
switch (m_lSamplingArithmetic)
{
default:
ASSERT(FALSE);
break;
case Arithmetic_Douglas_Peuker:
Douglas_Peuker(sampling_pts.begin(), sampling_pts.end()-1, result_ppts, 1.5);
break;
case Arithmetic_McMaster:
McMaster(sampling_pts, 3.14159 / 20, result_ppts);
break;
}
result_ppts->push_back(sampling_pts[sampling_pts.size()-1]);
}
}

void CScribbleTool::SiftPts(const Vertexs& sampling_pts, Vertexs& result_pts)
{
result_pts.clear();
if (sampling_pts.size() < 3)
{
result_pts.assign(sampling_pts.begin(), sampling_pts.end());
}
else
{
Vertexs_CIter begin(sampling_pts.begin()), end(sampling_pts.end());
result_pts.push_back(*begin);
Vertexs_CIter index = sampling_pts.begin() + 1;
while (*index == *begin && index != end)
index++;
if (index != end)
{
result_pts.push_back(*index++);
for (; index != end; index++)
{
if (MathHelper::PointLine(*index, *(result_pts.end()-1), *result_pts.end()) > 1
&& *index != *result_pts.end())
result_pts.push_back(*index);
}
}
}
}

// Douglas-Peuker算法
void CScribbleTool::Douglas_Peuker(Vertexs_CIter begin, Vertexs_CIter end,
VertexsPtr result_ppts, const double delta)
{
ASSERT(result_ppts);

if (distance(begin, end) < 2)
return;

double dbLen = 0.0;
Vertexs_CIter index = FindFurthestPt(begin, end, dbLen);
if (dbLen > delta)
{
Douglas_Peuker(begin, index, result_ppts, delta);
result_ppts->push_back(*index);
Douglas_Peuker(index, end, result_ppts, delta);
}
}

CScribbleTool::Vertexs_CIter CScribbleTool::FindFurthestPt(Vertexs_CIter begin, Vertexs_CIter end,
double& dbLen)
{
ASSERT(distance(begin, end) > 1);

Vertexs_CIter index = begin + 1;
dbLen = MathHelper::PointLine(*begin, *end, *index);
for (Vertexs_CIter s = index+1; s < end-1; s++)
{
double dbTemp = MathHelper::PointLine(*begin, *end, *s);
if (dbTemp > dbLen)
{
dbLen = dbTemp;
index = s;
}
}
return index;
}

// McMaster算法
void CScribbleTool::McMaster(const Vertexs& sampling_pts, const double delta,
VertexsPtr result_ppts)
{
ASSERT(sampling_pts.size() > 3);

Vertexs_CIter end(sampling_pts.end()-1);
CPoint cStartPt = *(sampling_pts.begin());
for (Vertexs_CIter index = sampling_pts.begin()+1; index < end; index++)
{
double dbAngle = fabs(MathHelper::AngleOf3Pts(cStartPt, *(index+1), *index));
if (dbAngle > delta)
{
result_ppts->push_back(*index);
cStartPt = *index;
}
}
}
梧桐168 2004-08-01
  • 打赏
  • 举报
回复
#pragma once

class CScribbleTool
{
#define Arithmetic_Douglas_Peuker 0
#define Arithmetic_McMaster 1

public:
CScribbleTool(void) : m_bScribbling(FALSE), m_lSamplingArithmetic(Arithmetic_McMaster){}
~CScribbleTool(void){}

const long GetCurArithmetic() const { return m_lSamplingArithmetic; }
void SetCurArithmetic(const long lSamplingArithmetic){m_lSamplingArithmetic = lSamplingArithmetic;}
void OnMouseMove(const CPoint& pt, CView& cView);
void OnLButtonDown(const CPoint& pt, CView& cView);
void OnLButtonUp(const CPoint& pt, CView& cView);

protected:
typedef vector<CPoint> Vertexs;
typedef Vertexs* VertexsPtr;
typedef Vertexs::iterator Vertexs_Iter;
typedef Vertexs::const_iterator Vertexs_CIter;

void Sampling(const Vertexs& sampling_pts, VertexsPtr result_ppts);
void SiftPts(const Vertexs& sampling_pts, Vertexs& result_pts);
// Douglas_Peuker系列函数
void Douglas_Peuker(Vertexs_CIter begin, Vertexs_CIter end, VertexsPtr result, const double delta);
Vertexs_CIter FindFurthestPt(Vertexs_CIter begin, Vertexs_CIter end, double& dbLen);
// McMaster系列函数
void McMaster(const Vertexs& sampling_pts, const double delta, VertexsPtr result_ppts);

protected:
BOOL m_bScribbling;
long m_lSamplingArithmetic;
Vertexs m_cVertexs;
};
梧桐168 2004-07-30
  • 打赏
  • 举报
回复
我用的是最简单的道格拉斯算法(只考虑点到线距离),后来有人提出了新的同时考虑角度和长度的算法
zzwu 2004-07-30
  • 打赏
  • 举报
回复
问题是,任意曲线就应包括非常复杂的不规则曲线,而这种曲线上可删的冗余点可能不多。

不过我上面的例也举的不对,至少不确切,因为五边形的边长有大小,应该指出边长才行.考虑下面的五边形:

.

**
* *
*

这里点与点间距离为1或根号2,就无法抽去任何点了.
梧桐168 2004-07-30
  • 打赏
  • 举报
回复
我现在用的算法,无论折线多么复杂,也不会走样。
梧桐168 2004-07-30
  • 打赏
  • 举报
回复
“打个比喻,要从一个五边形中抽取3个点连接起来,就无论如何不会再像五边形了"

你这句话,就是曲解采样的原意了,采样并不一定是删的点越多越好,而是在保证外形
基本不变的情况下,删除那些冗余的点。

WORD的自由曲线应该用的就是这个方法。
zzwu 2004-07-30
  • 打赏
  • 举报
回复
打个比喻,要从一个五边形中抽取3个点连接起来,就无论如何不会再像五边形了.
zzwu 2004-07-30
  • 打赏
  • 举报
回复
要从几千个点抽取200个点(更少也可),如果曲线简单,不会有问题的,
但如果曲线原始形状很复杂, 就不可能保持原形了.
iamredeye 2004-07-30
  • 打赏
  • 举报
回复
我有同学说用遗传算法,越来越麻烦了喔,个人感觉GIS的采样算法听起来比较象样啊,各位高人别按兵不动了,发表一下意见呐
iamredeye 2004-07-29
  • 打赏
  • 举报
回复
能不能给点提示,我该到哪里找这些资料呢?或者该搜索什么关键字?更或者你能给我发点这方面的文档吗?我对这些一点也没概念。谢谢
加载更多回复(1)
内容简介: 无论你是从事业务开发,还是从事架构设计,想要优化设计模式,数据结构与算法是必备的一门学科,本课程使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。为什么学数据结构与算法算法是一个程序员真正的核心竞争力。无论用哪种语言做开发,算法从程序角度而言都是灵魂内核般的存在。程序的躯体可以各式各样,但是内核一定要追求高效整洁。同时掌握了算法,大厂名企的Offer不再是梦寐以求的梦想,而让程序高效且健壮,也不再是难以完成的技术难题。所以无论是为提升自我内功修炼,还是提升程序灵魂内核健全,学习算法,都是现有可供选项里的最优解。课程大纲:为了让大家快速系统了解数据结构与算法知识全貌,我为你总结了「数据结构与算法框架图」,帮你梳理学习重点,建议收藏!! CSDN学院Java答疑群:

4,446

社区成员

发帖
与我相关
我的任务
社区描述
图形图像/机器视觉
社区管理员
  • 机器视觉
  • 迪菲赫尔曼
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧