Timus 1006. Square frames

解析Timus1006.Squareframes问题,此程序可找出构成给定图形的方框序列,支持方框相互覆盖的情况。
Timus 1006. Square frames 要求根据屏幕上的图形给出一个能构成该图形的方框序列。

1006. Square frames

Time Limit: 2.0 second
Memory Limit: 16 MB
Frame consists of the following characters:
Problem illustration
N square frames (1 ≤ N ≤ 15) were sequentially drawn on screen 50 characters wide 20 lines tall. If parts of some frames intersect, only the part of the frame drawn latter remains visible. Each frame lies fully on the screen.
You need to write a program that builds a possible sequence of frames that (if drawn sequentially) would produce the same picture on the screen. Your sequence does not have to be the same with the original sequence used to build the picture on the screen. However, it should not contain more than 2000 frames.

Input

Problem illustration
The screen area was originally filled with dots (ASCII 46). Input contains the final picture on the screen after the sequence of frames is drawn.

Output

Your program should write to output the number of frames in the sequence built and the frames coordinates as follows:
K
X 1 Y 1 A 1

Xk Yk Ak
Here K is the number of frames, Xi and Yi are coordinates of the upper left frame corner (0 ≤ Xi ≤ 49, 0 ≤ Yi ≤ 19) and Ai is the length of the frame side (2 ≤ Ai). All numbers must be delimited with one or more spaces and/or line breaks.

Sample

inputoutput
(see the figure above) 6
16 11 7
32 14 4
4 8 8
11 6 7
36 11 3
28 8 3
Problem Source: USU Championship 1997

解答如下:

  1  using  System;
  2  using  System.IO;
  3  using  System.Text;
  4  using  System.Collections.Generic;
  5 
  6  namespace  Skyiv.Ben.Timus
  7  {
  8     //   http://acm.timus.ru/problem.aspx?space=1 &num=1006
  9     sealed   class  T1006
 10    {
 11       struct  Frame
 12      {
 13         static   readonly   char  Marked  =   ' * ' ;
 14         static   readonly   char  Background  =   ' . ' ;
 15         static   readonly   char  Vertical  =  ( char ) 179 ;
 16         static   readonly   char  Horizontal  =  ( char ) 196 ;
 17         static   readonly   char  LeftUpper  =  ( char ) 218 ;
 18         static   readonly   char  RightUpper  =  ( char ) 191 ;
 19         static   readonly   char  LeftBottom  =  ( char ) 192 ;
 20         static   readonly   char  RightBottom  =  ( char ) 217 ;
 21        
 22         public   static   readonly   char [] Uppers  =   new   char [] { LeftUpper, RightUpper };
 23         public   static   readonly   char [] Lefts  =   new   char [] { LeftUpper, LeftBottom };
 24 
 25         int  row, col, len;
 26        
 27        Frame( int  row,  int  col,  int  len)
 28        {
 29           this .row  =  row;
 30           this .col  =  col;
 31           this .len  =  len;
 32        }
 33        
 34         public   static  Frame GetValue( char  c,  int  row,  int  col,  int  len)
 35        {
 36           if  (c  ==  LeftUpper)  return   new  Frame(row, col, len);
 37           if  (c  ==  RightUpper)  return   new  Frame(row, col  -  len, len);
 38           if  (c  ==  LeftBottom)  return   new  Frame(row  -  len, col, len);
 39           if  (c  ==  RightBottom)  return   new  Frame(row  -  len, col  -  len, len);
 40           throw   new  ArgumentOutOfRangeException( " c " );
 41        }
 42        
 43         public   override   string  ToString()
 44        {
 45           return  col  +   "   "   +  row  +   "   "   +  (len  +   1 ); 
 46        }
 47        
 48         public   static   bool  IsCorner( char  c)
 49        {
 50           return  c  ==  LeftUpper  ||  c  ==  RightUpper  ||  c  ==  LeftBottom  ||  c  ==  RightBottom;
 51        }
 52        
 53         public   static   bool  IsHorizontal( char  c)
 54        {
 55           return  c  ==  Horizontal;
 56        }
 57        
 58         public   static   bool  IsVertical( char  c)
 59        {
 60           return  c  ==  Vertical;
 61        }
 62        
 63         public   static   bool  IsBackground( char  c)
 64        {
 65           return  c  ==  Background;
 66        }
 67        
 68         public   bool  InScreen( char [,] screen)
 69        {
 70           return  row  >=   0   &&  row  +  len  <  screen.GetLength( 0 )
 71               &&  col  >=   0   &&  col  +  len  <  screen.GetLength( 1 );
 72        }
 73        
 74         public   bool  Search(Stack < Frame >  stack,  char [,] screen)
 75        {
 76           if  ( ! IsFrame(screen))  return   false ;
 77          Mark(screen);
 78          stack.Push( this );
 79           return   true ;
 80        }
 81        
 82         bool  IsFrame( char [,] screen)
 83        {
 84           for  ( int  i  =   1 ; i  <  len; i ++ )
 85          {
 86             if  ( ! IsBorderLine(Vertical, screen, i,  0 ))  return   false ;
 87             if  ( ! IsBorderLine(Vertical, screen, i, len))  return   false ;
 88             if  ( ! IsBorderLine(Horizontal, screen,  0 , i))  return   false ;
 89             if  ( ! IsBorderLine(Horizontal, screen, len, i))  return   false ;
 90          }
 91           if  ( ! IsBorderLine(LeftUpper, screen,  0 0 ))  return   false ;
 92           if  ( ! IsBorderLine(RightUpper, screen,  0 , len))  return   false ;
 93           if  ( ! IsBorderLine(LeftBottom, screen, len,  0 ))  return   false ;
 94           if  ( ! IsBorderLine(RightBottom, screen, len, len))  return   false ;
 95           return   true ;
 96        }
 97        
 98         bool  IsBorderLine( char  c,  char [,] screen,  int  dy,  int  dx)
 99        {
100           char  ch  =  screen[row  +  dy, col  +  dx];
101           return  ch  ==  Marked  ||  ch  ==  c;
102        }
103        
104         void  Mark( char [,] screen)
105        {
106           for  ( int  i  =   0 ; i  <=  len; i ++ )
107            screen[row  +  i, col]  =  screen[row  +  i, col  +  len]  =
108            screen[row, col  +  i]  =  screen[row  +  len, col  +  i]  =  Marked;
109        }
110      }
111      
112       static   void  Main()
113      {
114         using  (TextReader reader  =   new  StreamReader(
115          Console.OpenStandardInput(), Encoding.GetEncoding( " iso-8859-1 " )))
116        {
117           new  T1006().Run(reader, Console.Out);
118        }
119      }
120      
121       void  Run(TextReader reader, TextWriter writer)
122      {
123         char [,] screen  =  Read(reader);
124        Stack < Frame >  stack  =   new  Stack < Frame > ();
125        SearchCorner(stack, screen);
126        SearchSide(stack, screen);
127        writer.WriteLine(stack.Count);
128         foreach  (Frame frame  in  stack) writer.WriteLine(frame);
129      }
130      
131       char [,] Read(TextReader reader)
132      {
133        List < string >  list  =   new  List < string > ();
134         for  ( string  s; (s  =  reader.ReadLine())  !=   null ; ) list.Add(s);
135         char [,] v  =   new   char [list.Count, list[ 0 ].Length];
136         for  ( int  i  =   0 ; i  <  list.Count; i ++ )
137           for  ( int  j  =   0 ; j  <  list[i].Length; j ++ )
138            v[i, j]  =  list[i][j];
139         return  v;
140      }
141 
142       void  SearchCorner(Stack < Frame >  stack,  char [,] screen)
143      {
144        begin:
145         for  ( int  i  =   0 ; i  <  screen.GetLength( 0 ); i ++ )
146           for  ( int  j  =   0 ; j  <  screen.GetLength( 1 ); j ++ )
147          {
148             if  ( ! Frame.IsCorner(screen[i, j]))  continue ;
149             for  ( int  len  =   1 ; ; len ++ )
150            {
151              Frame frame  =  Frame.GetValue(screen[i, j], i, j, len);
152               if  ( ! frame.InScreen(screen))  break ;
153               if  (frame.Search(stack, screen))  goto  begin;
154            }
155          }
156      }
157      
158       void  SearchSide(Stack < Frame >  stack,  char [,] screen)
159      {
160        begin:
161         for  ( int  i  =   0 ; i  <  screen.GetLength( 0 ); i ++ )
162           for  ( int  j  =   0 ; j  <  screen.GetLength( 1 ); j ++ )
163             if  (SearchVertical(stack, screen, i, j))  goto  begin;
164             else   if  (SearchHorizontal(stack, screen, i, j))  goto  begin;
165      }
166      
167       bool  SearchVertical(Stack < Frame >  stack,  char [,] screen,  int  row,  int  col)
168      {
169         if  ( ! Frame.IsVertical(screen[row, col]))  return   false ;
170         for  ( int  k  =  row  -   1 ; k  >=   0 ; k -- )
171        {
172           if  (Frame.IsBackground(screen[k, col])  ||  Frame.IsHorizontal(screen[k, col]))  break ;
173           foreach  ( char  c  in  Frame.Uppers)
174             for  ( int  len  =  row  -  k  +   1 ; ; len ++ )
175            {
176              Frame frame  =  Frame.GetValue(c, k, col, len);
177               if  ( ! frame.InScreen(screen))  break ;
178               if  (frame.Search(stack, screen))  return   true ;
179            }
180        }
181         return   false ;
182      }
183      
184       bool  SearchHorizontal(Stack < Frame >  stack,  char [,] screen,  int  row,  int  col)
185      {
186         if  ( ! Frame.IsHorizontal(screen[row, col]))  return   false ;
187         for  ( int  k  =  col  -   1 ; k  >=   0 ; k -- )
188        {
189           if  (Frame.IsBackground(screen[row, k])  ||  Frame.IsVertical(screen[row, k]))  break ;
190           foreach  ( char  c  in  Frame.Lefts)
191             for  ( int  len  =  col  -  k  +   1 ; ; len ++ )
192            {
193              Frame frame  =  Frame.GetValue(c, row, k, len);
194               if  ( ! frame.InScreen(screen))  break ;
195               if  (frame.Search(stack, screen))  return   true ;
196            }
197        }
198         return   false ;
199      }
200    }
201  }

这道题目比较有意思,它在屏幕上给出了一张画有一些正方形方框的图形,这些方框可能互相覆盖,但不会超出屏幕的边界。现有要求你给出一个能构成该图形的方框序列。

本程序的入口点 Main 方法位于第 112 到 119 行。请注意第 114 到 115 行将输入流的编码设定为 iso-8859-1,这是因为这道题目使用最高位为 1 的 ASCII 码来表示方框线。如果使用缺省的 UTF-8 编码将无法正确读取题目的输入。

第 121 到 129 行的 Run 方法执行实际的工作。首先在第 123 行调用第 131 到 140 行的 Read 方法读取输入(请注意该方法可以读取任意大小的矩形屏幕的内容)。然后在第 124 行分配一个用来保存各个方框的堆栈 stack, 接着在第 125 到 126行依次调用 SearchCorner 和 SearchSide 方法进行搜索。最后在第 127 到 128 行输出保存在堆栈 stack 中的结果。

第 142 到 156 行的 SearchCorner 方法从方框的四个角开始在全屏幕进行搜索。第 148 行判断如果屏幕当前元素不是方框的四个角的话就跳过。第 149 到 154 行的循环依次从边长为 1 开始递增构造方框(第 151 行),直到方框超出屏幕为止(第 152 行)。第 153 行调用 Search 方法来进行搜索,如果成功地找到并标记一个方框,就跳回第 144 行重新开始搜索。注意这并不会造成死循环,因为找到的方框已经被标记过了,下次就不会再找这个方框了。这里使用了 goto 语句,因为这是很清楚自然的做法。如果要消除这个 goto 语句的话,势必要增加布尔变量形成复杂的控制流程,反而不如使用 goto 语句一目了然。

第 74 到 80 行的 Search 方法首先在第 76 行调用 IsFrame 方法判断能否构成一个正方形方框,如果可以的话,就在 77 行调用 Mark 方法标记该方框,然后将该方框压入堆栈(第 78 行)。

第 82 到 96 行的 IsFrame 方法首先在第 84 到 90 行判断构成方框的边线是否符合要求,然后在第 91 到 94 行判断构成方框的四个角是否符合要求。这是通过调用第 98 到 102 行的 IsBorderLine 方法进行判断的,如果该位置是标记过的也算符合要求,因为这表明这个位置原来是被其他方框覆盖了的。

第 104 到 109 行的 Mark 方法用来标记已经找到的方框,以防止下次搜索时再次找到同一方框陷入死循环。

第 158 到 165 行的 SearchSide 方法从方框的边线开始全屏幕搜索。在第 163 到 164 分别调用 SearchVertical 和 SearchHorizontal 来进行搜索,如果成功地找到并标记一个方框,就跳回第 160 行重新开始搜索。

第 167 到 182 行的 SearchVertical 方法在 169 行从方框的垂直线开始搜索。在 170 行开始从当前位置往上搜索直到屏幕顶部为止。如果碰到水平线或屏幕背景就停止搜索(第 172 行),因为这个方框肯定是被其它方框覆盖的,它不能在这次搜索中压入堆栈,必须留待以后处理。然后在第 173 行开始使用两个顶角试图构造方框(第 176 行),其边长从 row - k + 1 开始依次增大(第 174 行),直到方框超出屏幕为止(第 177 行)。第 178 行调用 Search 方法来进行搜索。

第 184 到 199 行到 SearchHorizontal 方法从方框的水平线开始搜索。它的工作原理和 SearchVertical 方法是一样的。


返回目录
void Mapserver::closed_loop_detection_graph(int max_frame_size, bool use_graph_pose) { // 更新进度 std::stringstream _mesg; _mesg << "开始优化位姿,截止帧ID:" << max_frame_size - 1; // ROS_INFO_STREAM(_mesg.str();); sendBuildMessageToPC(14, "信息", "建图程序", "更新地图过程中", _mesg, 0, 10); // 初始化闭环 //points_map_.loop_edge_size = 0; // 闭环的帧 frame _closed_frame; // 闭环的帧 int _closed_frame_index; // 最好i评分 double _best_score; // 距离平方 double _distance_sqr; // 级别差值 int _diffe_level; /// @brief 弧度差距 double _diffe_yaw; // 评分 double _score; // 先复位帧 // if (false) // { // // 用来恢复初值 // points_map_.frames[0].odom = points_map_.frames[0].pose; // for (int i = 0; i < max_frame_size && i < points_map_.frame_size; ++i) // { // // 没有固定的恢复初数值 // if (i > points_map_.fixed_frame_index) // points_map_.frames[i].odom = points_map_.frames[points_map_.frames_edge[i].seq_i].odom * points_map_.frames_edge[i].measure; // else // 固定的就算pose // points_map_.frames[i].odom = points_map_.frames[i].pose; // } // std::stringstream _mesg2; // _mesg2 << "位姿优化复位结束,截止帧ID:" << max_frame_size - 1; // sendBuildMessageToPC(14, "信息", "建图程序", "更新地图过程中", _mesg2, 0, 10); // } // int _diffe_level_min = (floor)(5.5 / mapping_params_.distance_step); // 6米内的都参与计算 double _max_sqr = mapping_params_.loop_search_range; //_max_sqr=6.9; //_max_sqr = 5.2; // 以下代码为小闭环用的代码 // int _diffe_level_min = (ceil)(_max_sqr / mapping_params_.distance_step * 1.1); // if (_diffe_level_min > 9) // _diffe_level_min = 9; int _diffe_level_min = mapping_params_.loop_min_level; //_diffe_level_min=3; //_diffe_level_min = 5; //// // double _max_sqr = 3.6; double _dis_max_sqr = _max_sqr * _max_sqr; std::vector<Eigen::Vector2i> _near_frames; bool small_close_loop=false; //for (int i_1 = 0; i_1 < max_frame_size && i_1 < points_map_.frame_size; i_1+=5) for (int i_1 = 0; i_1 < max_frame_size && i_1 < points_map_.frame_size; ++i_1) { if(i_1%2==0) small_close_loop=true; else small_close_loop=false; // 三级后再开始找回环,并且不是长巷道,帧也没有被弃用 if (points_map_.frames[i_1].level > _diffe_level_min && points_map_.frames[i_1].status != 3 && points_map_.frames[i_1].status != 7&&points_map_.frames[i_1].close_index==0) // points_map_.frames[i_1].num_links == 1 && { _near_frames.clear(); // 初始化评分 _best_score = 0; _score = 0; // 轮训其他帧,找到合适闭环的 int _i_2_max = std::min(max_frame_size, points_map_.frame_size); if(small_close_loop) _i_2_max=i_1-1; //_i_2_max = i_1 - _diffe_level_min+1; //for (int i_2 = 1; i_2 < _i_2_max; i_2+=5) if(points_map_.frames[i_1].closeloop_num<=3) for (int i_2 = 1; i_2 < _i_2_max; ++i_2) { // 同一个点跳过 if (i_1 == i_2) continue; // 弃用帧跳过 if (points_map_.frames[i_2].status == 7) continue; // 长巷道跳过 // if (points_map_.frames[i_2].status!=3) // continue; // 是取回环前的位子还是回环后的位子 if (!use_graph_pose) _distance_sqr = points_map_.frames[i_1].pose.getSquare(points_map_.frames[i_2].pose); else _distance_sqr = points_map_.frames[i_1].pose.getSquare(points_map_.frames[i_2].pose); _diffe_yaw = points_map_.frames[i_1].pose.getYaw() - points_map_.frames[i_2].pose.getYaw() ; /// 角度差异也加入运算 if(_diffe_yaw>M_PI)_diffe_yaw-=M_PI; else if(_diffe_yaw<=-M_PI)_diffe_yaw+=M_PI; _diffe_yaw=abs(_diffe_yaw); _diffe_level = points_map_.frames[i_1].level - points_map_.frames[i_2].level; /// 往前闭合 if (small_close_loop) _diffe_level_min = 4; else _diffe_level_min = mapping_params_.loop_min_level; if (small_close_loop) _dis_max_sqr = 4.5 * 4.5 ; else _dis_max_sqr = _max_sqr * _max_sqr; // 距离小于3.6米的才闭环,节点级别差N代 // ROS_INFO_STREAM("_distance_sqr"<<_distance_sqr<<"_diffe_level"<<_diffe_level); if (_distance_sqr < _dis_max_sqr && abs(_diffe_level) >= _diffe_level_min) { // 目标级别差越好,距离越近评分越高 // _score = abs(_diffe_level) * 13.0 + (_max_sqr - sqrt(_distance_sqr)) * 10.0-_diffe_yaw*20.0; _score = abs(_diffe_level) * 13.0 + (_max_sqr - sqrt(_distance_sqr)) * 50.0-_diffe_yaw*20.0; /// 单雷达最看重角度 if(mapping_params_.scanner_num==1) _score = abs(_diffe_level) * 13.0 + (_max_sqr - sqrt(_distance_sqr)) * 50.0-_diffe_yaw*40.0; /// 如果_diffe_level>0,表示目标级别低,时间早,精度高,闭环效果好,则增加个固定评分 if(_diffe_level>0) _score+=100.0; Eigen::Vector2i _near_frame = Eigen::Vector2i(i_2, (int)_score); _near_frames.push_back(_near_frame); if (_score > _best_score) { _closed_frame = points_map_.frames[i_2]; _closed_frame_index = i_2; _best_score = _score; } } } else if(points_map_.frames[i_1].close_index>=1) { _near_frames.push_back(Eigen::Vector2i(points_map_.frames[i_1].close_index,100)); } if (_near_frames.size() > 0) { // 降顺序排列 std::sort(_near_frames.begin(), _near_frames.end(), [](Eigen::Vector2i a, Eigen::Vector2i b) { return a(1) > b(1); }); bool _scan_matcher_ok = false; // 设置帧建匹配过来的关键帧作为点云 std::shared_ptr<frame> _source_cloud_ptr = std::make_shared<frame>(points_map_.frames[i_1]); scan_matcher_build_->setSourceCloud(_source_cloud_ptr); // 限制在10个以内 for (int i_2 = 0; i_2 < _near_frames.size() && i_2 <= 8; ++i_2) { scan_matcher_build_->initMatch(); _closed_frame_index = _near_frames[i_2](0); std::shared_ptr<frame> _target_cloud_ptr = std::make_shared<frame>(points_map_.frames[_closed_frame_index]); scan_matcher_build_->setTargetCloud(_target_cloud_ptr); PoseWithCov _deviation; // if (!use_graph_pose) // _deviation = points_map_.frames[_closed_frame_index].pose.getmeasurement(points_map_.frames[i_1].pose); // else //好像反了 _deviation = points_map_.frames[_closed_frame_index].pose.getmeasurement(points_map_.frames[i_1].pose); //_deviation = points_map_.frames[i_1].pose.getmeasurement(points_map_.frames[_closed_frame_index].pose); scan_matcher_build_->setInitialGuass(_deviation); /// 进行一次ICP的匹配 if(small_close_loop) scan_matcher_build_->match(); else scan_matcher_build_->match(true); // 长巷道,不进行闭环 if (scan_matcher_build_->result_.probability_straight < 0.92 && scan_matcher_build_->result_.success && scan_matcher_build_->result_.health > mapping_params_.loop_success_threshold*1.2) { if (points_map_.loop_edge_size < points_map_.frame_capacity && points_map_.loop_edge_size < points_map_.loop_edge.size()) { scan_matcher_build_small_->initMatch(); scan_matcher_build_small_->setSourceCloud(_source_cloud_ptr); scan_matcher_build_small_->setTargetCloud(_target_cloud_ptr); scan_matcher_build_small_->setInitialGuass(scan_matcher_build_->result_.pose); scan_matcher_build_small_->match(); if (scan_matcher_build_small_->result_.success && scan_matcher_build_small_->result_.health > mapping_params_.loop_success_threshold) { // 添加回环边 measurement edge_temp; edge_temp.seq_i = _closed_frame_index; edge_temp.seq_j = i_1; edge_temp.measure = scan_matcher_build_small_->result_.pose; //edge_temp.infoMatrix = Eigen::Matrix3d::Identity(); edge_temp.infoMatrix = scan_matcher_build_small_->result_.infoMatrix; points_map_.loop_edge[points_map_.loop_edge_size] = edge_temp; // points_map_.loop_edge[points_map_.loop_edge_size].infoMatrix = Eigen::Matrix3d::Identity();; points_map_.loop_edge_size++; _scan_matcher_ok = true; points_map_.frames[i_1].close_index = _closed_frame_index; // ROS_INFO_STREAM("建图程序,回环成功:ID:" << i_1 << " 与ID:" << _closed_frame_index << " ,匹配健康值:" << scan_matcher_build_->result_.health); break; } } else { ROS_ERROR_STREAM("建图:严重错误:闭环边数量超出容量"); } } // 释放 _target_cloud_ptr.reset(); } _source_cloud_ptr.reset(); if (points_map_.frames[i_1].closeloop_num < 1000) points_map_.frames[i_1].closeloop_num++; // if (!_scan_matcher_ok) // ROS_WARN_STREAM("建图程序,回环失败,两帧匹配不上:ID:" << i_1 << " 与ID:" << _closed_frame_index << " ,匹配健康值:" << scan_matcher_build_->result_.health); } //// 闭合后的点云不再进行闭合 // if (points_map_.frames[i_1].close_index == 0) // points_map_.frames[i_1].close_index = 99999; // // 有闭环的点 // if (_best_score > 0) // { // bool _scan_matcher_ok = false; // // 设置帧建匹配过来的关键帧作为点云 // boost::shared_ptr<frame> _source_cloud_ptr = boost::make_shared<frame>(points_map_.frames[i_1]); // scan_matcher_build_->setSourceCloud(_source_cloud_ptr); // boost::shared_ptr<frame> _target_cloud_ptr = boost::make_shared<frame>(points_map_.frames[_closed_frame_index]); // scan_matcher_build_->setTargetCloud(_target_cloud_ptr); // PoseWithCov _deviation = points_map_.frames[_closed_frame_index].pose.getmeasurement(points_map_.frames[i_1].pose); // scan_matcher_build_->setInitialGuass(_deviation); // /// 进行一次ICP的匹配 // scan_matcher_build_->match(); // if (scan_matcher_build_->result_.success && scan_matcher_build_->result_.health > mapping_params_.loop_success_threshold) // { // if (points_map_.loop_edge_size < points_map_.frame_capacity && points_map_.loop_edge_size < points_map_.loop_edge.size()) // { // // 添加回环边 // measurement edge_temp; // edge_temp.seq_i = _closed_frame_index; // edge_temp.seq_j = i_1; // edge_temp.measure = scan_matcher_build_->result_.result; // edge_temp.infoMatrix = Eigen::Matrix3d::Identity(); // points_map_.loop_edge[points_map_.loop_edge_size] = edge_temp; // points_map_.loop_edge_size++; // _scan_matcher_ok = true; // ROS_INFO_STREAM("建图程序,回环成功:ID:" << i_1 << " 与ID:" << _closed_frame_index << " ,匹配健康值:" << scan_matcher_build_->result_.health); // } // else // { // ROS_ERROR_STREAM("建图程序,回环严重错误:回环边数量:" << points_map_.loop_edge_size << " 大于等于预设容量:" << points_map_.frame_capacity << " ,或大于等于实际容量:" << points_map_.loop_edge.size()); // } // } // if (!_scan_matcher_ok) // ROS_WARN_STREAM("建图程序,回环失败,两帧匹配不上:ID:" << i_1 << " 与ID:" << _closed_frame_index << " ,匹配健康值:" << scan_matcher_build_->result_.health); // }; } if (i_1 > 50 && i_1 % 20 == 0) { // 进度从10到30 int _step = 10 + i_1 * 20 / max_frame_size; // 更新进度 std::stringstream _mesg2(""); //_mesg.clear(); _mesg2 << "优化位姿,预处理中,截止帧ID:" << max_frame_size - 1; sendBuildMessageToPC(14, "信息", "建图程序", "更新地图过程中", _mesg2, 0, _step); } } /// 有强制闭环的帧的ID输入,表示需要强制闭环 if (A_frame_ID_force_loop_ != B_frame_ID_force_loop_ && A_frame_ID_force_loop_ >= 0 && B_frame_ID_force_loop_ >= 0 && A_frame_ID_force_loop_ < points_map_.frame_size && B_frame_ID_force_loop_ < points_map_.frame_size) { int _A_frame_ID = A_frame_ID_force_loop_; int _B_frame_ID = B_frame_ID_force_loop_; double _search_range_force_loop = (double)search_range_force_loop_; A_frame_ID_force_loop_ = 0; B_frame_ID_force_loop_ = 0; forceLoop(_A_frame_ID, _B_frame_ID, _search_range_force_loop); // if(forceLoop(_A_frame_ID, _B_frame_ID)) // { // //扩大上限 // max_frame_size=std::max(max_frame_size, std::max(_A_frame_ID+1,_B_frame_ID+1)); // //max_loop_edge_size=std::max(max_loop_edge_size, points_map_.loop_edge_size); // } } if (points_map_.loop_edge_size > 0) { double graph_residual = 0; // 图优化 std::stringstream _mesg3; _mesg3 << "优化位姿,大量计算中,请耐心等待,截止帧ID:" << max_frame_size - 1; sendBuildMessageToPC(14, "信息", "建图程序", "更新地图过程中", _mesg3, 0, 40); bool _use_aruco = false; if (status_aruco_frame_ == 1) _use_aruco = true; ////帧间匹配 current_Iteration_ceres_ = 0; int _result = ceres_graph_method(points_map_.fixed_frame_index, graph_results_, points_map_, graph_residual, current_Iteration_ceres_, max_frame_size, points_map_.loop_edge_size, true, _use_aruco,auto_flat_k_,mapping_params_.num_threads); current_Iteration_ceres_ = -1; if (_result > 0) { std::stringstream _mesg4(""); _mesg4 << "位姿优化完成,截止帧ID:" << max_frame_size - 1; sendBuildMessageToPC(14, "信息", "建图程序", "更新地图过程中", _mesg4, 0, 60); } else { std::stringstream _mesg4(""); _mesg4 << "位姿优化失败!失败!失败!无法优化位姿,截止帧ID:" << max_frame_size - 1; sendBuildMessageToPC(14, "错误", "建图程序", "更新地图过程中", _mesg4, 0, 60); std::stringstream _mesg5(""); _mesg5 << "位姿优化失败!失败!失败!无法优化位姿,截止帧ID:" << max_frame_size - 1; sendBuildMessageToPC(0, "重要错误", "建图程序", "更新地图过程中", _mesg5, 30, 0); } new_graph_pose_input_update_ = true; // if (points_map_.fixed_frame_index < max_frame_size - 1) // { // ceres_graph_method(points_map_.fixed_frame_index, graph_results_, points_map_, graph_residual, max_frame_size, max_edge_size, use_graph_pose); // std::stringstream _mesg4(""); // _mesg4 << "建图程序,进度:60%,帧间优化完成"; // sendBuildMessageToPC(4, _mesg4); // new_graph_pose_input_update_ = true; // } // else // { // std::stringstream _mesg4(""); // _mesg4 << "建图程序,进度:60%,所有帧固定,不进行帧优化"; // sendBuildMessageToPC(4, _mesg4); // new_graph_pose_input_update_ = true; // } } else { std::stringstream _mesg4(""); _mesg4 << "没有回环,位姿优化失败"; sendBuildMessageToPC(14, "信息", "建图程序", "更新地图过程中", _mesg4, 0, 60); new_graph_pose_input_update_ = true; } // // 线程内处理上一次的结果,现移到外部 // if (new_graph_pose_input_update_) // { // // for (int i = 0; i < graph_results_.size() && i < points_map_.frame_size && i < points_map_.frames.size(); ++i) // // { // // PoseWithCov _measurement(graph_results_[i]); // // points_map_.frames[i].pose_graph = _measurement; // // } // // change_graph_ = false; // } return; }
08-29
import sensor import time sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time=2000) clock = time.clock() while True: clock.tick() img = sensor.snapshot() print(clock.fps()) import sensor, image, math # 初始化传感器 sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) # 灰度模式 sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time = 2000) # 已知参数 actual_square_size = 50 # 正方形实际边长 (mm) camera_focal_length = 2.8 # 相机焦距 (mm) def find_smallest_square(img): # 设置二值化阈值(黑白分割) binary_threshold = [(100, 0)] # 图像二值化 img.binary(binary_threshold) # 查找所有轮廓 blobs = img.find_blobs(binary_threshold, pixels_threshold=100, area_threshold=100, merge=True) smallest_square = None min_area = float('inf') for blob in blobs: if len(blob.corners()) == 4: # 只考虑四边形 area = blob.w() * blob.h() if area < min_area: min_area = area smallest_square = blob return smallest_square def calculate_distance(square_side_length): # 计算距离 distance = (actual_square_size * camera_focal_length) / square_side_length return distance while(True): img = sensor.snapshot() # 寻找最小的正方形 square = find_smallest_square(img) if square: # 绘制最小的正方形 img.draw_rectangle(square.rect(), color=(255, 0, 0)) # 计算边长 side_length = square.w() # 或者square.h(),因为是正方形 # 计算距离 distance = calculate_distance(side_length) print("边长: %d pixels, 距离: %.2f mm" % (side_length, distance)) else: print("未找到正方形") 可以把中间实心黑色几何物变成黑色,用白色屏蔽周围颜色,用托马斯算法算出最小正方形的边长,框选出最小正方形
08-03
# %% [markdown] # # CNN-VIT 视频动态手势识别 # # ### 隔空手势 # %% [markdown] # ### 下载数据 # %% # import os # import moxing as mox # if not os.path.exists('hand_gesture'): # mox.file.copy_parallel('obs://modelbox-course/hand_gesture', 'hand_gesture') # %% [markdown] # ### 准备环境 # %% # conda clean -i -y # %% # conda install cudatoolkit=11.3.1 cudnn=8.2.1 -y --override-channels --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main # %% # !pip install --upgrade pip # %% # !pip install tensorflow-gpu==2.5.0 imageio # %% # !pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple # %% # conda install tqdm matplotlib==3.5.3 # %% [markdown] # 运行完成请务必点击左上角Kernel->Restart Kernel重启kernel # %% [markdown] # ### 模型训练 # %% import cv2 import glob import numpy as np from tqdm import tqdm import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import matplotlib.pyplot as plt %matplotlib inline # %% [markdown] # 打印TensorFlow版本并显示可用GPU # %% print('Tensorflow version: {}'.format(tf.__version__)) print('GPU available: {}'.format(tf.config.list_physical_devices('GPU'))) # %% [markdown] # 创建视频输入管道获取视频类别标签 # %% videos = glob.glob('hand_gesture/*.mp4') np.random.shuffle(videos) labels = [int(video.split('_')[-2]) for video in videos] videos[:5], len(videos), labels[:5], len(videos) # %% [markdown] # 显示数据分布情况 # %% from collections import Counter counts = Counter(labels) print(counts) plt.figure(figsize=(8, 4)) plt.bar(counts.keys(), counts.values()) plt.xlabel('Class label') plt.ylabel('Number of samples') plt.title('Class distribution in videos') plt.show() # %% [markdown] # 图像中心裁剪 # %% def crop_center_square(img): h, w = img.shape[:2] square_w = min(h, w) start_x = w // 2 - square_w // 2 end_x = start_x + square_w start_y = h // 2 - square_w // 2 end_y = start_y + square_w result = img[start_y:end_y, start_x:end_x] return result # %% MAX_SEQUENCE_LENGTH = 40 IMG_SIZE = 299 NUM_FEATURES = 1536 # %% [markdown] # 视频抽帧预处理 # %% def load_video(file_name): cap = cv2.VideoCapture(file_name) # 每隔多少帧抽取一次 frame_interval = 4 frames = [] count = 0 while True: ret, frame = cap.read() if not ret: break # 每隔frame_interval帧保存一次 if count % frame_interval == 0: # 中心裁剪 frame = crop_center_square(frame) # 缩放 frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE)) # BGR -> RGB [0,1,2] -> [2,1,0] frame = frame[:, :, [2, 1, 0]] frames.append(frame) count += 1 return np.array(frames) # %% [markdown] # 显示视频 # %% import random import imageio from IPython.display import Image label_to_name = {0:'无效手势', 1:'上滑', 2:'下滑', 3:'左滑', 4:'右滑', 5:'打开', 6:'关闭', 7:'放大', 8:'缩小'} print(label_to_name.get(labels[0])) frames = load_video(videos[0]) frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8) imageio.mimsave('test.gif', frames, durations=10) display(Image(open('test.gif', 'rb').read())) frames.shape # %% [markdown] # #### InceptionResNetV2 # %% [markdown] # 创建图像特征提取器 # %% def get_feature_extractor(): feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2( weights = 'imagenet', include_top = False, pooling = 'avg', input_shape = (IMG_SIZE, IMG_SIZE, 3) ) preprocess_input = keras.applications.inception_resnet_v2.preprocess_input inputs = keras.Input((IMG_SIZE, IMG_SIZE, 3)) preprocessed = preprocess_input(inputs) outputs = feature_extractor(preprocessed) model = keras.Model(inputs, outputs, name = 'feature_extractor') return model # %% feature_extractor = get_feature_extractor() feature_extractor.summary() # %% # from tensorflow.keras.applications import InceptionResNetV2 # weights_path = 'inception_resnet_v2_weights_tf_dim_ordering_tf_kernels_notop.h5' # model = InceptionResNetV2(weights=None, include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3)) # model.load_weights(weights_path) # feature_extractor = model # feature_extractor.summary() # %% [markdown] # 提取视频图像特征 # %% def load_data(videos, labels): video_features = [] for video in tqdm(videos): frames = load_video(video) counts = len(frames) # 如果帧数小于MAX_SEQUENCE_LENGTH if counts < MAX_SEQUENCE_LENGTH: # 补白 diff = MAX_SEQUENCE_LENGTH - counts # 创建全0的numpy数组 padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # 数组拼接 frames = np.concatenate((frames, padding)) # 获取前MAX_SEQUENCE_LENGTH帧画面 frames = frames[:MAX_SEQUENCE_LENGTH, :] # 批量提取特征 video_feature = feature_extractor.predict(frames) video_features.append(video_feature) return np.array(video_features), np.array(labels) # %% video_features, classes = load_data(videos, labels) video_features.shape, classes.shape # %% [markdown] # #### Dataset # %% batch_size = 16 dataset = tf.data.Dataset.from_tensor_slices((video_features, classes)) dataset = dataset.shuffle(len(videos)) test_count = int(len(videos) * 0.2) train_count = len(videos) - test_count dataset_train = dataset.skip(test_count).cache().repeat() dataset_test = dataset.take(test_count).cache().repeat() train_dataset = dataset_train.shuffle(train_count).batch(batch_size) test_dataset = dataset_test.shuffle(test_count).batch(batch_size) train_dataset, train_count, test_dataset, test_count # %% [markdown] # #### VIT Model # %% # 位置编码 class PositionalEmbedding(layers.Layer): def __init__(self, seq_length, output_dim): super().__init__() # 构造从0~MAX_SEQUENCE_LENGTH的列表 self.positions = tf.range(0, limit=MAX_SEQUENCE_LENGTH) self.positional_embedding = layers.Embedding(input_dim=seq_length, output_dim=output_dim) def call(self,x): # 位置编码 positions_embedding = self.positional_embedding(self.positions) # 输入相加 return x + positions_embedding # %% # 编码器 class TransformerEncoder(layers.Layer): def __init__(self, num_heads, embed_dim): super().__init__() self.p_embedding = PositionalEmbedding(MAX_SEQUENCE_LENGTH, NUM_FEATURES) self.attention = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1) self.layernorm = layers.LayerNormalization() def call(self,x): # positional embedding positional_embedding = self.p_embedding(x) # self attention attention_out = self.attention( query = positional_embedding, value = positional_embedding, key = positional_embedding, attention_mask = None ) # layer norm with residual connection output = self.layernorm(positional_embedding + attention_out) return output # %% def video_cls_model(class_vocab): # 类别数量 classes_num = len(class_vocab) # 定义模型 model = keras.Sequential([ layers.InputLayer(input_shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)), TransformerEncoder(2, NUM_FEATURES), layers.GlobalMaxPooling1D(), layers.Dropout(0.1), layers.Dense(classes_num, activation="softmax") ]) # 编译模型 model.compile(optimizer = keras.optimizers.Adam(1e-5), loss = keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics = ['accuracy'] ) return model # %% # 模型实例化 model = video_cls_model(np.unique(labels)) # 打印模型结构 model.summary() # %% from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau # 保存检查点 checkpoint = ModelCheckpoint(filepath='best.h5', monitor='val_loss', save_weights_only=True, save_best_only=True, verbose=1, mode='min') # 提前终止 earlyStopping = EarlyStopping(monitor='loss', patience=50, mode='min', baseline=None) # 减少learning rate rlp = ReduceLROnPlateau(monitor='loss', factor=0.7, patience=30, min_lr=1e-15, mode='min', verbose=1) # %% [markdown] # #### 开始训练 # %% history = model.fit(train_dataset, epochs = 1000, steps_per_epoch = train_count // batch_size, validation_steps = test_count // batch_size, validation_data = test_dataset, callbacks = [checkpoint, earlyStopping, rlp]) # %% [markdown] # #### 绘制结果 # %% plt.plot(history.epoch, history.history['loss'], 'r', label='loss') plt.plot(history.epoch, history.history['val_loss'], 'g--', label='val_loss') plt.title('VIT Model') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() # %% plt.plot(history.epoch, history.history['accuracy'], 'r', label='acc') plt.plot(history.epoch, history.history['val_accuracy'], 'g--', label='val_acc') plt.title('VIT Model') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend() # %% [markdown] # 加载训练最优权重 # %% model.load_weights('best.h5') # %% [markdown] # 模型评估 # %% model.evaluate(dataset.batch(batch_size)) # %% [markdown] # 保存模型 # %% model.save('saved_model') # %% [markdown] # ### 手势识别 # %% import random # 加载模型 model = tf.keras.models.load_model('saved_model') # 类别标签 label_to_name = {0:'无效手势', 1:'上滑', 2:'下滑', 3:'左滑', 4:'右滑', 5:'打开', 6:'关闭', 7:'放大', 8:'缩小'} # %% # 获取视频特征 def getVideoFeat(frames): frames_count = len(frames) # 如果帧数小于MAX_SEQUENCE_LENGTH if frames_count < MAX_SEQUENCE_LENGTH: # 补白 diff = MAX_SEQUENCE_LENGTH - frames_count # 创建全0的numpy数组 padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # 数组拼接 frames = np.concatenate((frames, padding)) # 取前MAX_SEQ_LENGTH帧 frames = frames[:MAX_SEQUENCE_LENGTH,:] # 计算视频特征 N, 1536 video_feat = feature_extractor.predict(frames) return video_feat # %% # 视频预测 def testVideo(): test_file = random.sample(videos, 1)[0] label = test_file.split('_')[-2] print('文件名:{}'.format(test_file) ) print('真实类别:{}'.format(label_to_name.get(int(label))) ) # 读取视频每一帧 frames = load_video(test_file) # 挑选前帧MAX_SEQUENCE_LENGTH显示 frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8) # 保存为GIF imageio.mimsave('animation.gif', frames, duration=10) # 获取特征 feat = getVideoFeat(frames) # 模型推理 prob = model.predict(tf.expand_dims(feat, axis=0))[0] print('预测类别:') for i in np.argsort(prob)[::-1][:5]: print('{}: {}%'.format(label_to_name[i], round(prob[i]*100, 2))) return display(Image(open('animation.gif', 'rb').read())) # %% [markdown] # 视频推理 # %% for i in range(20): testVideo() 添加usb视频推理
07-24
import numpy as np import matplotlib.pyplot as plt import time import seaborn as sns from scipy.interpolate import interp1d # 设置随机种子保证结果可复现 np.random.seed(42) # 实验参数配置 class Config: TOTAL_TIME = 1.0 # 总仿真时间(秒) TARGET_VELOCITY = 5.0 # 切向速度(°/s) VERTICAL_ACCEL = 2.0 # 垂直加速度(°/s²) # 相机配置 FREQS = [50, 300] # 帧频(Hz) WINDOW_SIZES = [1.0, 1.0 / 3] # 开窗边长(°),300Hz窗口面积是50Hz的1/9 # 测量噪声参数 POSITION_NOISE_STD = 0.05 # 位置测量噪声标准差(°) SIMULATIONS = 20 # 蒙特卡洛仿真次数 cfg = Config() def generate_trajectory(dt, total_time): """ 生成目标运动轨迹 :param dt: 时间步长(秒) :param total_time: 总时间(秒) :return: 时间点数组, x位置数组, y位置数组 """ t = np.arange(0, total_time, dt) n = len(t) # 切向方向 - 匀速运动 x = cfg.TARGET_VELOCITY * t # 垂直方向 - 匀加速运动 (初始速度为0) y = 0.5 * cfg.VERTICAL_ACCEL * t ** 2 return t, x, y def linear_extrapolation(positions, dt): """ 线性外推预测算法 :param positions: 历史位置数组(N, 2) :param dt: 时间步长(秒) :return: 预测位置(x, y) """ if len(positions) < 2: return positions[-1] if len(positions) > 0 else (0, 0) # 计算速度向量(最后两帧) v = (positions[-1] - positions[-2]) / dt # 线性外推预测下一帧位置 prediction = positions[-1] + v * dt return prediction def simulate_tracking(freq_idx): """ 执行单次跟踪仿真 :param freq_idx: 频率索引(0=50Hz, 1=300Hz) :return: (计算时间列表, RMSE, 成功率) """ freq = cfg.FREQS[freq_idx] dt = 1.0 / freq window_size = cfg.WINDOW_SIZES[freq_idx] # 生成真实轨迹 t_true, x_true, y_true = generate_trajectory(dt, cfg.TOTAL_TIME) n_frames = len(t_true) # 初始化变量 compute_times = [] # 每帧计算时间 measured_positions = [] # 测量位置(带噪声) predicted_positions = [] # 预测位置 errors = [] # 位置误差 # 模拟跟踪过程 for i in range(n_frames): # 添加测量噪声 noise = np.random.normal(0, cfg.POSITION_NOISE_STD, 2) measured_pos = np.array([x_true[i], y_true[i]]) + noise measured_positions.append(measured_pos) # 开始计时(模拟计算复杂度) start_time = time.perf_counter() if i > 0: # 从第二帧开始预测 # 线性外推预测 prediction = linear_extrapolation( np.array(measured_positions[:i]), dt ) predicted_positions.append(prediction) # 计算位置误差 error = np.linalg.norm(prediction - np.array([x_true[i], y_true[i]])) errors.append(error) else: # 第一帧没有预测 predicted_positions.append(measured_pos) errors.append(0) # 结束计时 compute_time = time.perf_counter() - start_time compute_times.append(compute_time * 1000) # 转换为毫秒 # 计算成功率: 目标是否在预测窗口内 in_window = [] for i in range(1, n_frames): dx = abs(predicted_positions[i][0] - x_true[i]) dy = abs(predicted_positions[i][1] - y_true[i]) in_window.append(dx <= window_size / 2 and dy <= window_size / 2) success_rate = np.mean(in_window) * 100 if in_window else 0 rmse = np.sqrt(np.mean(np.square(errors[1:]))) if len(errors) > 1 else 0 # 返回每帧计算时间、RMSE和成功率 return compute_times, rmse, success_rate # 运行蒙特卡洛仿真 results = {freq: {'compute_times': [], 'rmse': [], 'success': []} for freq in cfg.FREQS} for _ in range(cfg.SIMULATIONS): for freq_idx, freq in enumerate(cfg.FREQS): compute_times, rmse, success = simulate_tracking(freq_idx) results[freq]['compute_times'].append(np.mean(compute_times)) results[freq]['rmse'].append(rmse) results[freq]['success'].append(success) # 计算平均结果 metrics = { freq: { 'avg_compute_time': np.mean(results[freq]['compute_times']), 'std_compute_time': np.std(results[freq]['compute_times']), 'avg_rmse': np.mean(results[freq]['rmse']), 'std_rmse': np.std(results[freq]['rmse']), 'avg_success': np.mean(results[freq]['success']), 'std_success': np.std(results[freq]['success']) } for freq in cfg.FREQS } # 打印结果 print("=" * 60) print(f"{'帧频(Hz)':<10} {'平均计算时间(ms)':<20} {'平均RMSE(°)':<15} {'平均成功率(%)'}") print("-" * 60) for freq in cfg.FREQS: m = metrics[freq] print(f"{freq:<10} {m['avg_compute_time']:.4f} ± {m['std_compute_time']:.4f} " f"{m['avg_rmse']:.4f} ± {m['std_rmse']:.4f} " f"{m['avg_success']:.2f} ± {m['std_success']:.2f}") print("=" * 60) # 可视化结果 sns.set(style="whitegrid") plt.figure(figsize=(15, 10)) # 1. 计算复杂度比较 plt.subplot(2, 2, 1) bars = plt.bar( [str(freq) for freq in cfg.FREQS], [metrics[freq]['avg_compute_time'] for freq in cfg.FREQS], yerr=[metrics[freq]['std_compute_time'] for freq in cfg.FREQS], capsize=5, color=['blue', 'orange'] ) plt.title('计算复杂度比较(平均每帧处理时间)') plt.ylabel('处理时间(ms)') plt.xlabel('相机帧频(Hz)') for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height, f'{height:.4f}', ha='center', va='bottom') # 2. 跟踪准确率(RMSE)比较 plt.subplot(2, 2, 2) bars = plt.bar( [str(freq) for freq in cfg.FREQS], [metrics[freq]['avg_rmse'] for freq in cfg.FREQS], yerr=[metrics[freq]['std_rmse'] for freq in cfg.FREQS], capsize=5, color=['blue', 'orange'] ) plt.title('跟踪准确率比较(RMSE)') plt.ylabel('均方根误差(°)') plt.xlabel('相机帧频(Hz)') for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height, f'{height:.4f}', ha='center', va='bottom') # 3. 跟踪成功率比较 plt.subplot(2, 2, 3) bars = plt.bar( [str(freq) for freq in cfg.FREQS], [metrics[freq]['avg_success'] for freq in cfg.FREQS], yerr=[metrics[freq]['std_success'] for freq in cfg.FREQS], capsize=5, color=['blue', 'orange'] ) plt.title('跟踪成功率比较') plt.ylabel('成功率(%)') plt.xlabel('相机帧频(Hz)') plt.ylim(0, 110) # 成功率范围为0-100% for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height, f'{height:.2f}%', ha='center', va='bottom') # 4. 综合性能比较 plt.subplot(2, 2, 4) metrics_names = ['计算时间(ms)', 'RMSE(°)', '成功率(%)'] x = np.arange(len(metrics_names)) width = 0.35 # 标准化指标以便比较 norm_compute = [metrics[50]['avg_compute_time'], metrics[300]['avg_compute_time']] norm_rmse = [metrics[50]['avg_rmse'], metrics[300]['avg_rmse']] norm_success = [metrics[50]['avg_success'], metrics[300]['avg_success']] plt.bar(x - width / 2, [norm_compute[0], norm_rmse[0], norm_success[0]], width, label='50Hz') plt.bar(x + width / 2, [norm_compute[1], norm_rmse[1], norm_success[1]], width, label='300Hz') plt.title('综合性能比较') plt.ylabel('指标值') plt.xticks(x, metrics_names) plt.legend() plt.tight_layout() plt.show() 这段代码中,PLT画图为何中文是乱码
09-06
clear; maxIter = 50; % cancerPA=importdata('BG1_Z48.mat'); % save BG1_Z480419.txt cancerPA; % H_SV = load ('T_BG1_Z48.mat'); file = 'T_BG1_Z48_new.txt'; M = 64; m_bits_per_s = log2(M); max_erorr_bits = 10000; max_frames = 1000; max_erorr_frames = 50; load("qam64_AE.mat") load("ATSC64.mat") constellation_AE = complex_values(:,1) + complex_values(:,2)*1i; constellation_ATSC = cons64(:,7); load("BG1_Z48.mat"); z = 48; K = 22 ; N = 46 ; r = 1/2; H_SV = H_SV(1:(N-K),1:N); pcmatrix = ldpcQuasiCyclicMatrix(z,H_SV); cfgLDPCDec = ldpcDecoderConfig(pcmatrix,"BP"); % 修改为BP,NMS库函数默认为分层NMS,想要与GPU版本对应上,需要用BP pun_pre = 2*z; pun_suf = 0; %llr_pre = zeros(pun_pre,1,"gpuArray"); %llr_suf = zeros(pun_suf,1,"gpuArray"); llr_pre = zeros(pun_pre,1); llr_suf = zeros(pun_suf,1); %% %for AssemblyParts = 500 %% Load Parity Check Matrix file H GPU XPU = 'GPU'; [h] = BLDPCRead(file); H = []; %for i = 1:AssemblyParts H = blkdiag(H, h); %end ldpcPropertyValuePairs = { 'MaximumIterationCount' , maxIter, ... 'ParityCheckMatrix' , H, ... 'DecisionMethod' , 'Soft decision', ... 'IterationTerminationCondition' ,'Parity check satisfied',... 'NumIterationsOutputPort', 1,'FinalParityChecksOutputPort',1}; if strcmp(XPU, 'CPU') HDec = comm.LDPCDecoder(ldpcPropertyValuePairs{:}); elseif strcmp(XPU, 'GPU') HDec = comm.gpu.LDPCDecoder(ldpcPropertyValuePairs{:}); end EbN0_db_qam = 5.5:0.2:7.8; % EbN0_db_qam = 10; for i= 1:1:length(EbN0_db_qam) frame_num = 0; erorr_bits = 0; error_frames = 0; erorr_bits_2 = 0; error_frames_2 = 0; EbN0 = 10 ^(EbN0_db_qam(i) / 10); sigma = 1/sqrt(2* log2(M)* r * EbN0); tic while(frame_num < max_frames && error_frames < max_erorr_frames) frame_num = frame_num + 1; %记录帧数 msg = randi(2,1,K*z)-1; codeword = nrldpc_encode(H_SV,z,msg); codeword_pun = codeword(pun_pre+1:end-pun_suf); tx = qammod(codeword_pun',M,'UnitAveragePower',1,'InputType','bit','PlotConstellation',0); noise = sigma * randn(size(tx)) + sigma*randn(size(tx)) * 1i; rx = tx + noise; llr = qamdemod(rx,M,"UnitAveragePower",1,"OutputType",'approxllr','NoiseVariance',sigma^2);%使用BP算法译码时,需要噪声方差,NMS可以不需要 % llr1= qamdemod(rx,M,"UnitAveragePower",1,"OutputType",'llr'); % diff=abs(llr-llr1)./abs(llr); % llr = gpuArray(qamdemod(rx,M,"UnitAveragePower",1,"OutputType",'approxllr')); llr = [llr_pre;llr;llr_suf]; %type = class(llr); [decode,a1,a2] = ldpcDecode_gpu(llr(1:N*z),cfgLDPCDec,maxIter,"DecisionType","soft"); decode = decode' < 0; e1 = sum(abs(decode - msg)); if e1>0 erorr_bits = erorr_bits + e1; error_frames = error_frames + 1; end %[decode,~,~] = ldpcDecode(llr(1:N*z),cfgLDPCDec,25); [decode_2,a3,a4] = step(HDec,llr(1:N*z)); %decode = gather(decode); decode_2 = decode_2' < 0; e2 = sum(abs(decode_2 - msg)); if e2>0 erorr_bits_2 = erorr_bits_2 + e2; error_frames_2 = error_frames_2 + 1; end %decode = reshape(decode, [N , 1]); %receivedBits = reshape(receivedBits, [N / AssemblyParts, AssemblyParts]); % each column is the short code % bits = sum(decode, 1); % error_frames = error_frames + sum(bits ~= 0); % erorr_bits = erorr_bits + sum(bits); end toc fer_qam_2(i) = error_frames_2/frame_num; ber_qam_2(i) = erorr_bits_2 /K/z/frame_num; %disp(ber_qam(i)) fer_qam(i) = error_frames/frame_num; ber_qam(i) = erorr_bits /K/z/frame_num; end markersize =6 ; linewidth = 0.9; xlabel("Eb/N0");ylabel("BER"); semilogy(EbN0_db_qam,ber_qam,"Marker", 'o',"MarkerSize",markersize,"LineStyle","-","Color",[0.8 0.1 0.1],"LineWidth",linewidth);hold on semilogy(EbN0_db_qam,ber_qam_2,"Marker", 'p',"Markersize",markersize,"LineStyle","-","Color",[0.5 0.4 0.1],"LineWidth",linewidth);hold on hold on if strcmp(XPU, 'CPU') legend_name2 = sprintf("BER-QAM-%s", "MATLAB Library function comm.LDPCDecoder Decoding result (CPU)"); % HDec = comm.LDPCDecoder(ldpcPropertyValuePairs{:}); elseif strcmp(XPU, 'GPU') legend_name2 = sprintf("BER-QAM-%s", "MATLAB Library function comm.gpu.LDPCDecoder Decoding result (GPU)"); % HDec = comm.gpu.LDPCDecoder(ldpcPropertyValuePairs{:}); end legend("BER-QAM-ldpcDecode\_gpu.m decoding result",legend_name2,"Location","southwest") grid on set(gca,'FontSize',14,'FontName','Times New Roman'); set(gca,'ytick',[1e-4,1e-3,1e-2,1e-1,1],'ygrid','on','gridlinestyle','-','Gridalpha',0.1); ylim([1e-4,1]) hold off %% % EbN0_db_AE = 5.6:0.2:6.4; % for i= 1:1:length(EbN0_db_AE) % frame_num = 0; % erorr_bits = 0; % error_frames = 0; % EbN0 = 10 ^(EbN0_db_AE(i) / 10); % sigma = 1/sqrt(2* log2(M)* r * EbN0); % while(frame_num < max_frames && error_frames < max_erorr_frames) % frame_num = frame_num + 1; %记录帧数 % msg = randi(2,1,K*z)-1; % codeword = nrldpc_encode(H_SV,z,msg); % codeword_pun = codeword(pun_pre+1:end-pun_suf); % tx = modulation(codeword_pun,M,0:1:M-1,constellation_AE); % noise = sigma * randn(size(tx)) + sigma*randn(size(tx)) * 1i; % rx = tx + noise; % llr = gpuArray(df_qamdemod(rx,M,de2bi(0:1:M-1,"left-msb"),constellation_AE)); % llr = reshape(llr,1,[])'; % llr = gather([llr_pre;llr;llr_suf]); % [decode,~,~] = ldpcDecode(llr(1:N*z),cfgLDPCDec,25,"DecisionType","soft"); % %[decode,~,~] = ldpcDecode(llr(1:N*z),cfgLDPCDec,25); % decode = decode' < 0; % e1 = sum(abs(decode - msg)); % if e1>0 % erorr_bits = erorr_bits + e1; % error_frames = error_frames + 1; % end % end % % fer_AE(i) = error_frames/frame_num; % ber_AE(i) = erorr_bits /K/z/frame_num; % disp(ber_AE(i)) % end % % %% % EbN0_db_ATSC = 5.6:0.2:6.4; % for i= 1:1:length(EbN0_db_ATSC) % frame_num = 0; % erorr_bits = 0; % error_frames = 0; % EbN0 = 10 ^(EbN0_db_ATSC(i) / 10); % sigma = 1/sqrt(2* log2(M)* r * EbN0); % while(frame_num < max_frames && error_frames < max_erorr_frames) % frame_num = frame_num + 1; %记录帧数 % msg = randi(2,1,K*z)-1; % codeword = nrldpc_encode(H_SV,z,msg); % codeword_pun = codeword(pun_pre+1:end-pun_suf); % tx = modulation(codeword_pun,M,0:1:M-1,constellation_ATSC); % noise = sigma * randn(size(tx)) + sigma*randn(size(tx)) * 1i; % rx = tx + noise; % llr = gpuArray(df_qamdemod(rx,M,de2bi(0:1:M-1,"left-msb"),constellation_ATSC)); % llr = reshape(llr,1,[])'; % llr = gather([llr_pre;llr;llr_suf]); % [decode,~,~] = ldpcDecode(llr(1:N*z),cfgLDPCDec,25,"DecisionType","soft"); % %[decode,~,~] = ldpcDecode(llr(1:N*z),cfgLDPCDec,25); % decode = decode' < 0; % e1 = sum(abs(decode - msg)); % if e1>0 % erorr_bits = erorr_bits + e1; % error_frames = error_frames + 1; % end % end % % fer_ATSC(i) = error_frames/frame_num; % ber_ATSC(i) = erorr_bits /K/z/frame_num; % disp(ber_ATSC(i)) % end % % %% % close % figure % % markersize =6 ; % linewidth = 0.9; % xlabel("Eb/N0");ylabel("BER"); % semilogy(EbN0_db_qam,ber_qam,"Marker", 'o',"MarkerSize",markersize,"LineStyle","-","Color",[0.8 0.1 0.1],"LineWidth",linewidth);hold on % semilogy(EbN0_db_AE,ber_AE,"Marker", 'p',"Markersize",markersize,"LineStyle","-","Color",[0.5 0.4 0.1],"LineWidth",linewidth);hold on % semilogy(EbN0_db_ATSC,ber_ATSC,"Marker", 'square',"MarkerSize",markersize,"LineStyle","-","Color",[0.1 0.1 0.8],"LineWidth",linewidth);hold on % legend("BER-QAM",'BER-AE','BER-ATSC',"Location","southwest") % grid on % set(gca,'FontSize',14,'FontName','Times New Roman'); % set(gca,'ytick',[1e-4,1e-3,1e-2,1e-1,1],'ygrid','on','gridlinestyle','-','Gridalpha',0.1); % ylim([1e-4,1]) % % hold off 解释并注释代码
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值