SDT旋转门压缩算法MFC图形测试

来源:互联网 发布:asp网站挂php商城 编辑:程序博客网 时间:2024/05/01 21:00

算法原理:


详细的解释:首先两扇门(由t0上的固定点,分别取上方和下方距该点E的距离取两个点形成的线段,看作上门和下门)处于关闭状态,也就是两扇门形成一条线与横向方向(t0->t8)垂直。现在加入新的点进来,上方点与新点形成新的直线斜率、下方点与新点也形成新的直线斜率。而核心就是:上门的斜率只能变大、下门的斜率只能变小,当新的点加入时,上门的斜率和下门的斜率均在变化,只能够朝一个方向变化。如果有一个新的点使得上门的斜率大于等于下门的斜率,即临界条件为双线平行时,那么这个新的点的上一个点为这一段数据表示的终点,即初始点和该点形成的线段可以粗略的标示该段区域内其它点的值(只要给出横向的数值,类同于y = kx + b),这在数学上称为离散分布,即真实的数据分布在线段区域的周围。因而,只需要两个点就可以大致的表示出这一区域所有的点,因而对于这种慢变化的曲线数据可以大大的减少实际数据量的大小,这就是整个算法的核心。


说明:该算法的压缩效果和数据有很大关系,一般测试时用正弦波数据,压缩效果很好,如果用随机数,基本不会压缩。

程序说明:

 1、m_Acc(压缩精度)的值要根据待压缩的数值来定,如果m_Acc的值太小,解压误差会很小,但压缩率低。

 2、如果m_Acc的值太大,压缩比非常高,100MB的文件可能就只有几M,但解压误差非常大。所以m_Acc要多试几次取一个比较合理的值。

 3、解压过程:(线性插值)有起点和终点坐标可以求出线段的公式,然后根据某点x轴坐标求出其对应的y的值,也就是实际的值。


下面给出VS2010测试该算法的完整C++实现代码:(作为该算法的核心模块)

1、sdt.cpp

#pragma once#include "StdAfx.h"#include <vector>struct point {<span style="white-space:pre"></span>int time;    double y;point(int new_time = 0, double new_y = 0.0) : time(new_time), y(new_y) {}};class sdt {double m_Acc;//压缩精度public:sdt(double m_Acc = 0) : m_Acc(m_Acc) {};void compress(std::vector<point>& undeal, std::vector<point>& comp) {//上门和下门,初始时门是关着的const double MAX_DOUBLE = 1.79769e+308;double slope1 = -MAX_DOUBLE;double slope2 = MAX_DOUBLE;      //当前数据的上斜率和下斜率double now_slope1, now_slope2;      double data;               //当前的数据double last_stored_data = undeal[0].y;   //最近保存的点double last_read_data = last_stored_data;<span style="white-space:pre"></span>//当前数据的前一个数据  //save the first datacomp.push_back(undeal[0]);      int last_stored_time = undeal[0].time;  //最近保存数据的时间      //循环处理数据int t;int size = undeal.size(), i;for( i = 1; i < size; ++i ) {t = undeal[i].time;data = undeal[i].y;now_slope1 = double(data - last_stored_data - m_Acc) / (t - last_stored_time);if(now_slope1 > slope1)//上门的斜率只能变大slope1 = now_slope1;  now_slope2 = double(data - last_stored_data + m_Acc) / (t - last_stored_time);if(now_slope2 < slope2)//下门的斜率只能变小slope2 = now_slope2;  if(slope1 >= slope2) {//当上门的斜率大于或等于下门的斜率时,即两门的夹角大于或等于180度//保存前一个点comp.push_back(undeal[i - 1]);              last_stored_time = undeal[i - 1].time; //修改最近保存数据时间点last_stored_data = undeal[i - 1].y;              //初始化两扇门为当前点与上个点的斜率slope1 = double(data - last_stored_data - m_Acc) / (t - last_stored_time);slope2 = double(data - last_stored_data + m_Acc) / (t - last_stored_time);}  last_read_data = data;}  // sava end pointcomp.push_back(undeal[i - 1]);}void uncompress(std::vector<point>& comp, std::vector<point>& dealt) {point a = comp[0],b;int i, size = comp.size();for(i = 1; i < size; ++i) {b = comp[i];//Step.1dealt.push_back(a);          //Step.2if(a.time + 1 != b.time) {double k = double(b.y - a.y) / (b.time - a.time); //计算斜率for(int j = a.time + 1; j < b.time; ++j) {//线性插值求被压缩掉的数据dealt.push_back(point(j, k * (j - a.time) + a.y));}}  a.time = b.time;a.y = b.y;}dealt.push_back(b);}  };

2、添加一个输入m_Acc数据的弹出式窗口类:new_acc,并新建一个弹出窗口,将其与这个类关联。

new_acc.h

#pragma once// new_acc 对话框class new_acc : public CDialogEx{DECLARE_DYNAMIC(new_acc)public:new_acc(CWnd* pParent = NULL);   // 标准构造函数virtual ~new_acc();// 对话框数据enum { IDD = IDD_DIALOG1 };protected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持DECLARE_MESSAGE_MAP()public:afx_msg void OnBnClickedOk();double m_acc;afx_msg void OnKillfocusEdit1();};

new_acc.cpp

#include "stdafx.h"#include "sdt_cs.h"#include "new_acc.h"#include "afxdialogex.h"// new_acc 对话框IMPLEMENT_DYNAMIC(new_acc, CDialogEx)new_acc::new_acc(CWnd* pParent /*=NULL*/): CDialogEx(new_acc::IDD, pParent){m_acc = 0.0;}new_acc::~new_acc(){}void new_acc::DoDataExchange(CDataExchange* pDX){CDialogEx::DoDataExchange(pDX);DDX_Text(pDX, IDC_EDIT1, m_acc);}BEGIN_MESSAGE_MAP(new_acc, CDialogEx)//ON_EN_CHANGE(IDC_EDIT1, &new_acc::OnEnChangeEdit1)ON_BN_CLICKED(IDOK, &new_acc::OnBnClickedOk)ON_EN_KILLFOCUS(IDC_EDIT1, &new_acc::OnKillfocusEdit1)//ON_COMMAND(ID_32771, &new_acc::OndialogAcc)END_MESSAGE_MAP()// new_acc 消息处理程序void new_acc::OnBnClickedOk(){// TODO: 在此添加控件通知处理程序代码UpdateData(true);AfxGetApp()->m_pMainWnd->Invalidate();CDialogEx::OnOK();}void new_acc::OnKillfocusEdit1(){// TODO: 在此添加控件通知处理程序代码CString str; GetDlgItem(IDC_EDIT1)->GetWindowText(str);char c;for(int i=0; i < str.GetLength(); ++i) { c = str.GetAt(i);if( c >= '0' && c <= '9' || c == '.')continue;else {MessageBox( "发现非法输入,请重新输入! ", "提示 ",MB_OK);break;}}}

3、启动时弹出输入m_Acc对话框,并且为把new_acc中的值传给m_Acc,所做的修改。

sdt_cs.h

// sdt_cs.h : sdt_cs 应用程序的主头文件//#pragma once#ifndef __AFXWIN_H__#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"#endif#include "resource.h"       // 主符号// Csdt_csApp:// 有关此类的实现,请参阅 sdt_cs.cpp//class Csdt_csApp : public CWinAppEx{public:Csdt_csApp();// 重写public:virtual BOOL InitInstance();virtual int ExitInstance();// 实现public:BOOL  m_bHiColorIcons;virtual void PreLoadState();virtual void LoadCustomState();virtual void SaveCustomState();afx_msg void OnAppAbout();DECLARE_MESSAGE_MAP()};extern Csdt_csApp theApp;

sdt_cs.cpp

<span style="font-size:18px;">// sdt_cs.cpp : 定义应用程序的类行为。//#include "stdafx.h"#include "afxwinappex.h"#include "afxdialogex.h"#include "sdt_cs.h"#include "MainFrm.h"</span><span style="font-family:Microsoft YaHei;font-size:12px;color:#ff0000;"><strong>#include "new_acc.h"</strong></span><span style="font-size:18px;">#ifdef _DEBUG#define new DEBUG_NEW#endif// Csdt_csAppBEGIN_MESSAGE_MAP(Csdt_csApp, CWinAppEx)ON_COMMAND(ID_APP_ABOUT, &Csdt_csApp::OnAppAbout)END_MESSAGE_MAP()// Csdt_csApp 构造Csdt_csApp::Csdt_csApp(){m_bHiColorIcons = TRUE;// 支持重新启动管理器m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;#ifdef _MANAGED// 如果应用程序是利用公共语言运行时支持(/clr)构建的,则://     1) 必须有此附加设置,“重新启动管理器”支持才能正常工作。//     2) 在您的项目中,您必须按照生成顺序向 System.Windows.Forms 添加引用。System::Windows::Forms::Application::SetUnhandledExceptionMode(System::Windows::Forms::UnhandledExceptionMode::ThrowException);#endif// TODO: 将以下应用程序 ID 字符串替换为唯一的 ID 字符串;建议的字符串格式//为 CompanyName.ProductName.SubProduct.VersionInformationSetAppID(_T("sdt_cs.AppID.NoVersion"));// TODO: 在此处添加构造代码,// 将所有重要的初始化放置在 InitInstance 中}// 唯一的一个 Csdt_csApp 对象Csdt_csApp theApp;</span><span style="font-family:Microsoft YaHei;font-size:14px;color:#ff0000;"><strong>new_acc acc;</strong></span><span style="font-size:18px;">// Csdt_csApp 初始化BOOL Csdt_csApp::InitInstance(){// 如果一个运行在 Windows XP 上的应用程序清单指定要// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,//则需要 InitCommonControlsEx()。否则,将无法创建窗口。INITCOMMONCONTROLSEX InitCtrls;InitCtrls.dwSize = sizeof(InitCtrls);// 将它设置为包括所有要在应用程序中使用的// 公共控件类。InitCtrls.dwICC = ICC_WIN95_CLASSES;InitCommonControlsEx(&InitCtrls);CWinAppEx::InitInstance();// 初始化 OLE 库if (!AfxOleInit()){AfxMessageBox(IDP_OLE_INIT_FAILED);return FALSE;}AfxEnableControlContainer();EnableTaskbarInteraction(FALSE);// 使用 RichEdit 控件需要  AfxInitRichEdit2()// AfxInitRichEdit2();// 标准初始化// 如果未使用这些功能并希望减小// 最终可执行文件的大小,则应移除下列// 不需要的特定初始化例程// 更改用于存储设置的注册表项// TODO: 应适当修改该字符串,// 例如修改为公司或组织名SetRegistryKey(_T("应用程序向导生成的本地应用程序"));InitContextMenuManager();InitKeyboardManager();InitTooltipManager();CMFCToolTipInfo ttParams;ttParams.m_bVislManagerTheme = TRUE;theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);// 若要创建主窗口,此代码将创建新的框架窗口// 对象,然后将其设置为应用程序的主窗口对象CMainFrame* pFrame = new CMainFrame;if (!pFrame)return FALSE;m_pMainWnd = pFrame;// 创建并加载框架及其资源pFrame->LoadFrame(IDR_MAINFRAME,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,NULL);// 唯一的一个窗口已初始化,因此显示它并对其进行更新pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();// 仅当具有后缀时才调用 DragAcceptFiles//  在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生</span><strong><span style="font-family:Microsoft YaHei;font-size:12px;color:#ff0000;">acc.DoModal();<span style="white-space:pre"></span>m_pMainWnd->Invalidate();</span></strong><span style="font-size:18px;">return TRUE;}int Csdt_csApp::ExitInstance(){//TODO: 处理可能已添加的附加资源AfxOleTerm(FALSE);return CWinAppEx::ExitInstance();}// Csdt_csApp 消息处理程序// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx{public:CAboutDlg();// 对话框数据enum { IDD = IDD_ABOUTBOX };protected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现protected:DECLARE_MESSAGE_MAP()public:};CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD){}void CAboutDlg::DoDataExchange(CDataExchange* pDX){CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)END_MESSAGE_MAP()// 用于运行对话框的应用程序命令void Csdt_csApp::OnAppAbout(){CAboutDlg aboutDlg;aboutDlg.DoModal();}// Csdt_csApp 自定义加载/保存方法void Csdt_csApp::PreLoadState(){BOOL bNameValid;CString strName;bNameValid = strName.LoadString(IDS_EDIT_MENU);ASSERT(bNameValid);GetContextMenuManager()->AddMenu(strName, IDR_POPUP_EDIT);}void Csdt_csApp::LoadCustomState(){}void Csdt_csApp::SaveCustomState(){}</span>

4、弹出对话框与画图

ChildView.h

// ChildView.h : CChildView 类的接口//#pragma once// CChildView 窗口class CChildView : public CWnd{// 构造public:CChildView();// 特性public:// 操作public:// 重写protected:virtual BOOL PreCreateWindow(CREATESTRUCT& cs);// 实现public:virtual ~CChildView();// 生成的消息映射函数protected:afx_msg void OnPaint();DECLARE_MESSAGE_MAP()public:afx_msg void OnDialogAcc();};

ChildView.cpp

// ChildView.cpp : CChildView 类的实现//#include "stdafx.h"#include "sdt_cs.h"#include "new_acc.h"#include "ChildView.h"#include <vector>#include <cmath>#include "sdt.cpp"#ifdef _DEBUG#define new DEBUG_NEW#endifextern new_acc acc;// CChildViewCChildView::CChildView(){}CChildView::~CChildView(){}BEGIN_MESSAGE_MAP(CChildView, CWnd)ON_WM_PAINT()ON_COMMAND(ID_DIALOG_ACC, &CChildView::OnDialogAcc)END_MESSAGE_MAP()// CChildView 消息处理程序BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) {if (!CWnd::PreCreateWindow(cs))return FALSE;cs.dwExStyle |= WS_EX_CLIENTEDGE;cs.style &= ~WS_BORDER;cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);return TRUE;}void CChildView::OnPaint() {CPaintDC dc(this); // 用于绘制的设备上下文// TODO: 在此处添加消息处理程序代码CRect rect;GetClientRect(&rect);int width = rect.Width();int height = rect.Height();int i, num;std::vector<point> undeal, compress, dealt;double maxX = 0.0;double minX = 0.0;double maxY = 0.0;double minY = 0.0;const double PI = 3.14159265358979323846;i = 0;for(double x_val = -2 * PI; x_val < 2 * PI; ++i, x_val += 0.2) {double sin_y = sin(x_val / 4);undeal.push_back(point(i, sin_y));if(maxX < i)maxX = i;else if(i < minX)minX = i;if(maxY < sin_y)maxY = sin_y;else if(sin_y < minY)minY = sin_y;}const int keep_top = 60;const int keep_bottom = 60;const int keep_left = 60;const int keep_right = 60;double intervalX=(width - keep_left - keep_right) / (maxX - minX);double intervalY=(height - keep_bottom - keep_top) / (maxY - minY);const int count = 10;//确定显示刻度个数//确定每个显示刻度之间的宽度double spaceX = (width - keep_left - keep_right) / count;double spaceY = (height - keep_bottom - keep_top) / count;CString str;//绘制X,Y轴,bottomY表示X轴的y值,leftX表示Y轴的x值double bottomY = 0;//X轴从图形区域最左端到最右端dc.MoveTo(int(keep_left), int(height - (keep_bottom + (bottomY - minY) * intervalY)));dc.LineTo(int(width - keep_right), int(height - (keep_bottom + (bottomY - minY) * intervalY)));//标识X轴每个刻度的值for(i = 0; i <= count; ++i) {str.Format("%.1f",minX + i * (maxX - minX) / count);dc.MoveTo(int(keep_left + spaceX * i), int(height - (keep_bottom + (bottomY - minY) * intervalY)));dc.LineTo(int(keep_left+spaceX*i), int(height-(keep_bottom+(bottomY-minY)*intervalY)+5));dc.TextOut(int(keep_left+spaceX*i-10), int(height-(keep_bottom+(bottomY-minY)*intervalY)+10), str);}double leftX = 0;//Y轴从图形区域最底端到最顶端dc.MoveTo(int(keep_left + (leftX - minX) * intervalX), int(height - keep_bottom));dc.LineTo(int(keep_left + (leftX - minX) * intervalX), int(keep_top));//标识Y轴每个刻度的值for(i=0; i <= count; ++i) {str.Format("%.1f",minY + i * (maxY - minY) / count);dc.MoveTo(int(keep_left + (leftX - minX) * intervalX), int(height - (keep_bottom + spaceY * i)));dc.LineTo(int(keep_left+(leftX - minX) * intervalX + 5), int(height - (keep_bottom + spaceY * i)));dc.TextOut(int(keep_left+(leftX-minX)*intervalX-30), int(height-(keep_bottom+spaceY*i+8)), str);}//绘制X,Y轴的变量名dc.TextOut( width / 2,height - 30,"X(time)");dc.TextOut(10, height / 5,"Y");CPen rpen(PS_SOLID, 2, RGB(255,0,0)), gpen(PS_SOLID, 2, RGB(0, 255, 0));//,bpen(PS_SOLID,2,RGB(0,255,0));dc.SelectObject(rpen);//未处理数据的曲线为红色标识//绘制未处理数据的曲线int rrdius = 2;dc.Ellipse(int(keep_left + (undeal[0].time - minX) * intervalX - rrdius), int(height + rrdius - (keep_bottom + (undeal[0].y - minY) * intervalY)),int(keep_left + (undeal[0].time - minX) * intervalX + rrdius), int(height - rrdius - (keep_bottom + (undeal[0].y - minY) * intervalY)));/*dc.MoveTo(int(keep_left + (undeal[0].time - minX) * intervalX),int(height - (keep_bottom + (undeal[0].y - minY) * intervalY)));*/num = undeal.size();for(i = 1; i < num; ++i) {/*dc.LineTo(int(keep_left + (undeal[i].time - minX) * intervalX),int(height - (keep_bottom+(undeal[i].y - minY) * intervalY)));*/dc.Ellipse(int(keep_left + (undeal[i].time - minX) * intervalX - rrdius), int(height + rrdius - (keep_bottom + (undeal[i].y - minY) * intervalY)),int(keep_left + (undeal[i].time - minX) * intervalX + rrdius), int(height - rrdius - (keep_bottom + (undeal[i].y - minY) * intervalY)));}sdt nsdt(acc.m_acc);nsdt.compress(undeal, compress);dc.SelectObject(gpen);//已压缩数据的曲线为绿色标识//绘制已压缩数据的曲线/*dc.MoveTo(int(keep_left + (compress[0].time - minX) * intervalX),int(height - (keep_bottom + (compress[0].y - minY) * intervalY)));*/int grdius = 2;dc.Ellipse(int(keep_left + (compress[0].time - minX) * intervalX - grdius), int(height - (keep_bottom + (compress[0].y - minY) * intervalY) + grdius),int(keep_left + (compress[0].time - minX) * intervalX + grdius), int(height - grdius - (keep_bottom + (compress[0].y - minY) * intervalY)));num = compress.size();for(i = 1; i < num; ++i) {dc.Ellipse(int(keep_left + (compress[i].time - minX) * intervalX - grdius), int(height - (keep_bottom + (compress[i].y - minY) * intervalY) + grdius),int(keep_left + (compress[i].time - minX) * intervalX + grdius), int(height - grdius - (keep_bottom + (compress[i].y - minY) * intervalY)));}str.Format("原生数据量为:%d,压缩后数据量为:%d", undeal.size(), compress.size());dc.TextOut(int(width / 2 - keep_right), int(keep_top / 6), str);str.Format("压缩比率为:%d / %d = %f", undeal.size(), compress.size(), (float)undeal.size() / compress.size());dc.TextOut(int(width / 2 - keep_right), int(keep_top / 6 + 20), str);/*nsdt.uncompress(compress, dealt);dc.SelectObject(bpen);//已处理数据的曲线为蓝色标识//绘制已处理数据的曲线dc.MoveTo(int(keep_left + (dealt[0].time - minX) * intervalX),int(height - (keep_bottom + (dealt[0].y - minY) * intervalY)));num = dealt.size();for(i = 1; i < num; ++i) {dc.LineTo(int(keep_left + (dealt[i].time - minX) * intervalX),int(height - (keep_bottom+(dealt[i].y - minY) * intervalY)));}*/// 不要为绘制消息而调用 CWnd::OnPaint()}void CChildView::OnDialogAcc(){// TODO: 在此添加命令处理程序代码acc.DoModal();}

5、其它的部分随项目自动生成。


6、运行截图:






1 0
原创粉丝点击