功能需求
实现超声中的M图(不用深究,理解后面我的数据流向就可以)的展示,和历史图像的播放。
上图中红色方框内为M图的截图,具体动画效果以数据流动想象(这个CSDN没搞懂这么传视频)。
M图有两种播放方式,分别为流动模式和覆盖模式。假设红色框的的显示宽度大小为5。
上图中的数字越大,代表越新的数据,以此类推,便可以达到通话效果。
还有一个是历史图像播放,等同于上图的一、二、三、四、五、六、七次的动画效果能够自由切换,此时我们需要记录更久之前的数据,所以我们需要一个更大存储空间(我这里用的list)存放更多的数据。此时我们就有了一个大list和一个小list,大list的大小表示最大存放历史数据大小,这个大小可调;小list等于这个播放窗口大小,例如上面的显示宽度5,那么我们小list的大小就为5,这个大小也可调。
实现一个什么呢
实现一个类来管理这两个list的读和写,而且读取出来的数据达到流动效果和覆盖效果(实现一种也可以)。
下面是我们实现的类,类中有一个不知道是不是bug的bug,有一个变量会一直加一,超出这个变量的最大值需要1年多的时间。
//H_ListFrameM.h
#ifndef H_LISTFRAMEM_H
#define H_LISTFRAMEM_H
#include "../frame/H_FrameM.h"
#include "../../../types/HSArray.h"
class H_ListFrameM
{
public:
H_ListFrameM();
~H_ListFrameM();
public:
// 设置最大帧
HSBool setMax(HSInt max);
// 设置滚动方式
HSBool setScroll(HSBool scroll);
// 获取滚动方式
HSBool scroll() const;
// 获取当前帧数
HSInt size(HSBool afterCut) const;
// 获取起始位置
HSInt start() const;
// 设置当前位置为起始位置
HSBool setStart();
// 设置起始位置
HSBool setStart(HSInt start);
// 获取结束位置
HSInt end() const;
// 设置当前位置为结束位置
HSBool setEnd();
// 设置结束位置
HSBool setEnd(HSInt end);
// 设置当前帧位置
HSBool setPos(HSInt pos);
// 设置当前帧位置
HSBool setPos(HSBool add, HSBool loop);
// 获取当前帧位置
HSInt posRead() const;
// 添加一帧
HSVoid push(H_HardData* hd);
// 获取第pos帧的图像
HSBool pop(HSInt pos, HSImage& image, HSUIntArray& stamp, HSInt blockW);
// 获取平均帧频
HSDouble avgFps();
// 释放内存(keepLastFrame=是否保留最后一帧)
HSVoid clear(HSBool keepLastFrame);
private:
HSBool alloc(const H_FrameM& frame);
HSVoid zero();
HSInt maxWidth();
HSInt posWrite();
HSBool posRead(HSInt pos, HSInt blockW, HSInt& src1, HSInt& dst1, HSInt& len1, HSInt& src2, HSInt& dst2, HSInt& len2, HSInt& src3, HSInt& dst3, HSInt& len3, HSInt& src4, HSInt& dst4, HSInt& len4);
HSVoid posReadScroll(HSInt blockW, HSInt start, HSInt& src1, HSInt& dst1, HSInt& len1, HSInt& src2, HSInt& dst2, HSInt& len2);
HSVoid posRead1(HSInt blockW, HSInt start, HSInt& src1, HSInt& dst1, HSInt& len1, HSInt& src2, HSInt& dst2, HSInt& len2, HSInt frees);
HSVoid posRead2(HSInt blockW, HSInt start, HSInt& src3, HSInt& dst3, HSInt& len3, HSInt& src4, HSInt& dst4, HSInt& len4, HSInt frees);
HSUShort* scanLine(HSInt x, HSInt y);
private:
HSBool m_scroll;
private:
HSInt m_max;
std::vector<HSUShort> m_list;
std::vector<HSUInt> m_stamp; // M图历史时间戳
HSInt64 m_write;
HSInt m_read;
HSInt m_start;
HSInt m_end;
private:
HSInt m_speed;
HSInt m_height;
private:
HSInt64 m_last; // M图历史计数器(用于计算平均帧频)
HSUInt m_time; // M图计时(用于计算平均帧频)
HSDouble m_avgfps; // M图平均帧频
};
#endif // H_LISTFRAMEM_H
//H_ListFrameM.cpp
#include "H_ListFrameM.h"
#include "../../../types/HSImage.h"
#include "../../../types/HS_Math.h"
#include "../../thread/H_Clock.h"
#define MAX_SIZE 4096
#define MAX_HEIGHT 512
H_ListFrameM::H_ListFrameM()
{
m_scroll = HSFalse;
m_max = 0;
m_write = -1;
m_read = -1;
m_start = -1;
m_end = -1;
m_speed = 0;
m_height = 0;
m_last = 0;
m_time = H_Clock::clock_ms();
m_avgfps = 0.0;
}
H_ListFrameM::~H_ListFrameM()
{
this->clear(HSFalse);
m_list.clear();
}
HSBool H_ListFrameM::setMax(HSInt max)
{
HSBool ok = HSFalse;
if ((max > 0) && (m_max != max))
{
m_max = max;
m_start = -1;
m_end = -1;
ok = HSTrue;
}
return ok;
}
HSBool H_ListFrameM::setScroll(HSBool scroll)
{
HSBool ok = HSFalse;
if (m_scroll != scroll)
{
m_scroll = scroll;
ok = HSTrue;
}
return ok;
}
HSBool H_ListFrameM::scroll() const
{
return m_scroll;
}
HSInt H_ListFrameM::size(HSBool afterCut) const
{
HSInt ok = 0;
if (!afterCut || (m_start < 0))
{
ok = HSMin(m_max, m_write + 1);
}
else
{
ok = (m_end - m_start + 1);
}
return ok;
}
HSInt H_ListFrameM::start() const
{
return HSMax(m_start, 0);
}
HSBool H_ListFrameM::setStart()
{
return this->setStart(m_read);
}
HSBool H_ListFrameM::setStart(HSInt start)
{
HSBool ok = HSFalse;
if (m_start != start)
{
if (start < this->size(HSFalse))
{
if (m_end < 0)
{
m_start = start;
m_end = this->size(HSFalse) - 1;
m_read = m_start;
}
else if (start <= m_end)
{
m_start = start;
}
else
{
m_start = m_end;
m_end = start;
}
ok = HSTrue;
}
}
return ok;
}
HSInt H_ListFrameM::end() const
{
return ((m_end < 0) ? this->size(HSFalse) - 1 : 0);
}
HSBool H_ListFrameM::setEnd()
{
return this->setEnd(m_read);
}
HSBool H_ListFrameM::setEnd(HSInt end)
{
HSBool ok = HSFalse;
if (m_end != end)
{
if (end < this->size(HSFalse))
{
if (m_start < 0)
{
m_end = end;
m_start = 0;
m_read = m_start;
}
else if (end >= m_start)
{
m_end = end;
}
else
{
m_end = m_start;
m_start = end;
}
ok = HSTrue;
}
}
return ok;
}
HSBool H_ListFrameM::setPos(HSInt pos)
{
HSBool ok = HSFalse;
if (m_read != pos)
{
m_read = pos;
ok = HSTrue;
}
return ok;
}
HSBool H_ListFrameM::setPos(HSBool add, HSBool loop)
{
HSBool ok = HSFalse;
if (m_write >= 0)
{
HSInt start = (m_start < 0) ? 0 : m_start;
HSInt end = (m_end < 0) ? this->size(HSFalse) - 1 : m_end;
HSUInt pos = HS_Math::getBound<HSInt>(m_read, start, end, 1, add, loop);
if (m_read != pos)
{
m_read = pos;
ok = HSTrue;
}
}
return ok;
}
HSInt H_ListFrameM::posRead() const
{
return m_read;
}
HSVoid H_ListFrameM::push(H_HardData* hd)
{
if (m_max && hd && hd->containsM())
{
H_FrameM frame(hd);
if (!frame.isEmpty())
{
if (this->alloc(frame))
{
HSInt posWrite = this->posWrite();
for (HSInt y=0; y<frame.m_height; y++)
{
HSUShort* src = frame.scanLine(y);
HSUShort* dst = this->scanLine(posWrite, y);
memmove(dst, src, m_speed * sizeof(HSUShort));
}
for (HSInt x=0; x<frame.m_speed; x++)
{
m_stamp[posWrite + x] = frame.m_stamp;
}
m_write = (m_write + 1);
m_read = this->size(HSFalse) - 1;
}
}
}
}
HSBool H_ListFrameM::pop(HSInt pos, HSImage& image, HSUIntArray& stamp, HSInt blockW)
{
HSBool ok = HSFalse;
if (m_write >= 0)
{
if (image.isEmpty() || (image.width() != blockW) || (image.height() != m_height))
{
image.create(blockW, m_height, HSImage::IT_16U_C1);
}
HSInt src1, dst1, len1, src2, dst2, len2, src3, dst3, len3, src4, dst4, len4;
if (this->posRead(pos, blockW, src1, dst1, len1, src2, dst2, len2, src3, dst3, len3, src4, dst4, len4))
{
if (len1)
{
for (HSInt y=0; y<m_height; y++)
{
HSUShort* src = (HSUShort*)this->scanLine(src1, y);
HSUShort* dst = (HSUShort*)image.scanLine(dst1, y);
memcpy(dst, src, len1 * sizeof(HSUShort));
}
}
if (len2)
{
for (HSInt y=0; y<m_height; y++)
{
HSUShort* src = (HSUShort*)this->scanLine(src2, y);
HSUShort* dst = (HSUShort*)image.scanLine(dst2, y);
memcpy(dst, src, len2 * sizeof(HSUShort));
}
}
if (len3)
{
for (HSInt y=0; y<m_height; y++)
{
HSUShort* src = (HSUShort*)this->scanLine(src3, y);
HSUShort* dst = (HSUShort*)image.scanLine(dst3, y);
memcpy(dst, src, len3 * sizeof(HSUShort));
}
}
if (len4)
{
for (HSInt y=0; y<m_height; y++)
{
HSUShort* src = (HSUShort*)this->scanLine(src4, y);
HSUShort* dst = (HSUShort*)image.scanLine(dst4, y);
memcpy(dst, src, len4 * sizeof(HSUShort));
}
}
stamp.resize(blockW);
if (len1) stamp.set(dst1, &m_stamp[src1], len1);
if (len2) stamp.set(dst2, &m_stamp[src2], len2);
if (len3) stamp.set(dst3, &m_stamp[src3], len3);
if (len4) stamp.set(dst4, &m_stamp[src4], len4);
}
ok = HSTrue;
}
return ok;
}
HSDouble H_ListFrameM::avgFps()
{
if (m_write >= 0)
{
HSUInt current = H_Clock::clock_ms();
// 时间有效
if (current - m_time >= (m_last ? 1000 : 0))
{
if (m_write - m_last >= (m_last ? 1 : 16))
{
HSInt count = (m_write - m_last) * m_speed;
HSDouble tc = 0.0;
for (HSUInt n=0; n<count-1; n++)
{
HSInt pos1 = (m_last * m_speed + n + 0) % this->maxWidth();
HSInt pos2 = (m_last * m_speed + n + 1) % this->maxWidth();
tc += m_stamp[pos2] - m_stamp[pos1];
}
// 保存平均帧频
if (tc > 0.0) m_avgfps = 1000.0 / (tc / count);
m_last = m_write;
m_time = current;
}
}
}
return m_avgfps;
}
HSVoid H_ListFrameM::clear(HSBool keepLastFrame)
{
if (keepLastFrame)
{
if (m_write >= 0)
{
// 回退到最后一帧前的位置
HSInt start = (m_write * m_speed) % this->maxWidth();
// 无需分段计算
for (HSInt y=0; y<m_height; y++)
{
memset(this->scanLine(0, y), 0, sizeof(HSUShort) * start);
memset(this->scanLine(start + m_speed, y), 0, sizeof(HSUShort) * (this->maxWidth() - (start + m_speed)));
}
memset(&m_stamp[0], 0, start * sizeof(HSUInt));
memset(&m_stamp[start + m_speed], 0, (this->maxWidth() - (start + m_speed)) * sizeof(HSUInt));
}
}
else
{
if (m_list.size())
{
this->zero();
m_write = -1;
m_read = -1;
m_start = -1;
m_end = -1;
m_last = 0;
m_time = H_Clock::clock_ms();
m_avgfps = 0.0;
}
}
}
HSBool H_ListFrameM::alloc(const H_FrameM& frame)
{
HSBool ok = HSFalse;
if (!frame.isEmpty())
{
if ((m_speed != frame.m_speed) || (m_height != frame.m_height))
{
m_speed = frame.m_speed;
m_height = frame.m_height;
m_list.resize(this->maxWidth() * m_height);
m_stamp.resize(this->maxWidth());
this->zero();
}
ok = HSTrue;
}
return ok;
}
HSVoid H_ListFrameM::zero()
{
memset(&m_list[0], 0, m_list.size() * sizeof(HSUShort));
memset(&m_stamp[0], 0, m_stamp.size() * sizeof(HSUShort));
}
HSInt H_ListFrameM::maxWidth()
{
return (m_speed * MAX_SIZE);
}
HSInt H_ListFrameM::posWrite()
{
HSInt ok = 0;
{
// 待写入位置乘以M超速度
ok = (m_write + 1) * m_speed;
// 格式化为圆形位置
ok %= this->maxWidth();
}
return ok;
}
HSBool H_ListFrameM::posRead(HSInt pos, HSInt blockW, HSInt& src1, HSInt& dst1, HSInt& len1, HSInt& src2, HSInt& dst2, HSInt& len2, HSInt& src3, HSInt& dst3, HSInt& len3, HSInt& src4, HSInt& dst4, HSInt& len4)
{
HSBool ok = HSFalse;
{
if (pos == -1) pos = m_read;
if (m_write + 1 >= m_max)
{
pos += (m_write + 1) - m_max;
}
if (m_scroll)
{
// 回退到目标图像宽度前的位置
HSInt start = (pos + 1) * m_speed - blockW;
// 分段计算
this->posReadScroll(blockW, start, src1, dst1, len1, src2, dst2, len2);
src3 = 0;
len3 = 0;
src4 = 0;
len4 = 0;
ok = HSTrue;
}
else
{
// 共循环了多少次(一次=目标图像宽度)
HSInt lines = (pos + 1) * m_speed / blockW;
// 又流动了多少线(小于目标图像宽度的部分)
HSInt frees = (pos + 1) * m_speed % blockW;
// 最后一次采集在目标图像宽度前的位置
HSInt current = (m_write + 1) * m_speed;
// 求最一次完整帧的起始位置
{
// 回退小于目标图像宽度的部分
HSInt start = current - frees;
// 若超出一次目标图像宽度,则继续回退一次,此时start为最后一次完整帧+覆盖部分前的位置
if (lines) start -= blockW;
// 格式化为有效部分(因为前面frees会再次覆盖,故后续无需复制,提前去掉)
start += frees;
// 分段计算
this->posRead1(blockW, start, src1, dst1, len1, src2, dst2, len2, frees);
}
// 求覆盖完整帧的起始位置
{
// 回退小于目标图像宽度的部分
HSInt start = current - frees;
// 分段计算
this->posRead2(blockW, start, src3, dst3, len3, src4, dst4, len4, frees);
}
ok = HSTrue;
}
}
return ok;
}
HSVoid H_ListFrameM::posReadScroll(HSInt blockW, HSInt start, HSInt& src1, HSInt& dst1, HSInt& len1, HSInt& src2, HSInt& dst2, HSInt& len2)
{
src1 = dst1 = len1 = src2 = dst2 = len2 = 0;
// 尚未采集满目标图像宽度
if (start < 0) start += this->maxWidth();
// 格式化为圆形位置
start %= this->maxWidth();
// 无跨越,一次性拷贝数据
if (start + blockW < this->maxWidth())
{
src1 = start;
dst1 = 0;
len1 = blockW;
}
// 跨越起始位置,需分段拷贝
else
{
src1 = start;
dst1 = 0;
len1 = this->maxWidth() - start;
src2 = 0;
dst2 = len1;
len2 = blockW - len1;
}
}
HSVoid H_ListFrameM::posRead1(HSInt blockW, HSInt start, HSInt& src1, HSInt& dst1, HSInt& len1, HSInt& src2, HSInt& dst2, HSInt& len2, HSInt frees)
{
src1 = dst1 = len1 = src2 = dst2 = len2 = 0;
// 尚未采集满目标图像宽度
if (start < 0) start += this->maxWidth();
// 格式化为圆形位置
start %= this->maxWidth();
// 无跨越,一次性拷贝数据
if (start + (blockW - frees) <= this->maxWidth())
{
src1 = start;
dst1 = frees;
len1 = blockW - frees;
}
// 跨越起始位置,需分段拷贝
else
{
src1 = start;
dst1 = frees;
len1 = this->maxWidth() - start;
src2 = 0;
dst2 = frees + len1;
len2 = (blockW - frees) - len1;
}
}
HSVoid H_ListFrameM::posRead2(HSInt blockW, HSInt start, HSInt& src3, HSInt& dst3, HSInt& len3, HSInt& src4, HSInt& dst4, HSInt& len4, HSInt frees)
{
src3 = dst3 = len3 = src4 = dst4 = len4 = 0;
// 尚未采集满目标图像宽度
if (start < 0) start += this->maxWidth();
// 格式化为圆形位置
start %= this->maxWidth();
// 无跨越,一次性拷贝数据
if (start + frees <= this->maxWidth())
{
src3 = start;
dst3 = 0;
len3 = frees;
}
// 跨越起始位置,需分段拷贝
else
{
src3 = start;
dst3 = 0;
len3 = this->maxWidth() - start;
src4 = 0;
dst4 = len3;
len4 = frees - len3;
}
}
HSUShort* H_ListFrameM::scanLine(HSInt x, HSInt y)
{
return &m_list[this->maxWidth() * y + x];
}
我为什么写这篇文章
集思广益,上面是我们的实现的源码,思路就不说了。自由思考,不一样的思路可能对于我们来说会更好。
实现思路
1.首先,管理一个大链表,链表采用圆形链表的思想,该链表两个功能,一是存放历史数据,而是给显示区域提供数据。
2.显示区域等同一个稍小的链表,链表中的数据来源是大链表,去大链表中取得对应的数据,在小链表中进行填充,填充的过程得符合流动和覆盖的方式。
3.最开始我的想法是在数据开始填充的开始,就是大链表中的数据还不能完全填充完小链表,这是一种情况,其次就是大链表未装满,但是能满足小链表的数据要求,在这两种情况下去寻找规律,这个画出示意图即可看出。
4.实现到上面部分,问题就来了。当大链表填充完了之后,大链表的赋值相当于重新开始,我的小链表的取值也跟着从头开始,这里就出现了问题,