CardiacUsImgRecogUtils.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #include <opencv2/opencv.hpp>
  2. #include <opencv2/core.hpp>
  3. #include <opencv2/imgcodecs.hpp>
  4. #include <algorithm>
  5. #include <iostream>
  6. #include <vector>
  7. #include <string>
  8. #include <filesystem>
  9. #include <stdexcept> // 包含异常头文件
  10. #include <chrono>
  11. #include <numeric>
  12. #include "ICardiacUsImgRecogUtils.h"
  13. // mission:输入为连续多帧的图像,输出为bool
  14. std::vector<cv::Mat> _frames;
  15. cv::Mat _prevFrameGray;
  16. std::vector < cv::Mat> _calculatedFlows;
  17. std::vector<double> _all_average_speed;
  18. std::vector<int> _len_pixels;
  19. std::vector<double> _speed_variances;
  20. int _count = 0;
  21. //// <summary>
  22. /// 去除离群点
  23. /// </summary>
  24. /// <param name="listData"></param>
  25. /// <returns></returns>
  26. std::vector<double> FunIQR(const std::vector<double> listData)
  27. {
  28. if (listData.empty())
  29. {
  30. // 处理空输入的情况
  31. return std::vector<double>();
  32. }
  33. // 复制输入数据以避免修改原始数据
  34. std::vector<double> copiedData = listData;
  35. // 计算第一四分位数(Q1)和第三四分位数(Q3)
  36. std::sort(copiedData.begin(), copiedData.end());
  37. size_t n = copiedData.size();
  38. double Q1 = copiedData[static_cast<size_t>(n * 0.25)];
  39. double Q3 = copiedData[static_cast<size_t>(n * 0.75)];
  40. // 计算IQR(四分位距)
  41. double IQR = Q3 - Q1;
  42. // 计算上限和下限,用于识别离群点
  43. double lowerBound = Q1 - 1.5 * IQR;
  44. double upperBound = Q3 + 1.5 * IQR;
  45. // 使用上限和下限来识别和移除离群点
  46. std::vector<double> filteredData;
  47. for (double x : copiedData)
  48. {
  49. if (lowerBound <= x && x <= upperBound)
  50. {
  51. filteredData.push_back(x);
  52. }
  53. }
  54. return filteredData;
  55. }
  56. /// <summary>
  57. /// 求中位数
  58. /// </summary>
  59. /// <param name="data"></param>
  60. /// <returns></returns>
  61. double Median(const std::vector<double>& data)
  62. {
  63. size_t n = data.size();
  64. if (n == 0)
  65. {
  66. // 返回 NaN 表示无效输入
  67. return std::nan("");
  68. }
  69. std::vector<double> sortedData = data; // 复制数据以进行中位数计算
  70. if (n % 2 == 1)
  71. {
  72. std::nth_element(sortedData.begin(), sortedData.begin() + n / 2, sortedData.end());
  73. return sortedData[n / 2];
  74. }
  75. else
  76. {
  77. std::nth_element(sortedData.begin(), sortedData.begin() + n / 2, sortedData.end());
  78. double median1 = sortedData[n / 2 - 1];
  79. double median2 = sortedData[n / 2];
  80. return (median1 + median2) / 2.0;
  81. }
  82. }
  83. void ResetPara()
  84. {
  85. _calculatedFlows.clear();
  86. _all_average_speed.clear();
  87. _len_pixels.clear();
  88. _speed_variances.clear();
  89. _prevFrameGray.release();
  90. _count = 0;
  91. }
  92. // 通用函数,用于删除vector的第一个元素
  93. template <typename T>
  94. void EraseFirstElementIfNotEmpty(std::vector<T>& vec)
  95. {
  96. if (!vec.empty())
  97. {
  98. vec.erase(vec.begin());
  99. }
  100. }
  101. // 移除值为-1的元素
  102. template <typename T>
  103. std::vector<T> RemoveNegativeOnes(const std::vector<T>& vec)
  104. {
  105. std::vector<T> result;
  106. for (const T& value : vec)
  107. {
  108. if (value != -1)
  109. {
  110. result.push_back(value);
  111. }
  112. }
  113. return result;
  114. }
  115. /// <summary>
  116. /// 判断是否是心脏超声图像
  117. /// </summary>
  118. /// <param name="imageData"></param>
  119. /// <param name="step"></param>
  120. /// <param name="isInitFrame"></param>
  121. /// <returns></returns>
  122. CardiacUsImgRecogStatus CheckIsCardiacUsImg(const StructImageInfo* imageData, const int step, bool isInitFrame)
  123. {
  124. const int imageWidth = imageData->Width;
  125. const int imageHeight = imageData->Height;
  126. int channel = imageData->Channel;
  127. const uint8_t* imagePData = imageData->PData;
  128. int imgSize = imageWidth * imageHeight;
  129. cv::Mat image = cv::Mat(cv::Size(imageWidth, imageHeight), CV_8UC3, (ushort*)imagePData);
  130. cv::resize(image, image, cv::Size(490, 325));
  131. cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
  132. if(isInitFrame)
  133. {
  134. ResetPara();
  135. }
  136. // 立即计算光流
  137. if (!_prevFrameGray.empty())
  138. {
  139. cv::Mat flow;
  140. cv::calcOpticalFlowFarneback(_prevFrameGray, image, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
  141. std::vector<double> speeds;
  142. std::vector<cv::Point2f> pixel_coordinates;
  143. for (int y = 0; y < flow.rows; y += step)
  144. {
  145. for (int x = 0; x < flow.cols; x += step)
  146. {
  147. const cv::Point2f& flow_at_point = flow.at<cv::Point2f>(y, x);
  148. double dx = flow_at_point.x;
  149. double dy = flow_at_point.y;
  150. double speed = std::sqrt(dx * dx + dy * dy);
  151. if (speed > 1.0)
  152. {
  153. speeds.push_back(speed); //单帧速度的集合
  154. //cv::arrowedLine(image, cv::Point(x, y), cv::Point(static_cast<int>(x + dx), static_cast<int>(y + dy)), cv::Scalar(255, 255, 255), 1);
  155. }
  156. }
  157. }
  158. if (!speeds.empty())
  159. {
  160. double average_speed = cv::mean(speeds)[0]; //单帧的速度均值
  161. double variance = 0.0;
  162. for (double value : speeds)
  163. {
  164. variance += (value - average_speed) * (value - average_speed);
  165. }
  166. variance /= speeds.size();
  167. _all_average_speed.push_back(average_speed); //多帧速度的集合
  168. _len_pixels.push_back(static_cast<int>(speeds.size())); //多帧,统计参与光流计算的像素点个数的集合
  169. _speed_variances.push_back(variance);
  170. //cv::putText(image, "var speed:" + std::to_string(variance), cv::Point(0, 30), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 255), 1);
  171. //cv::putText(image, "avg speed:" + std::to_string(average_speed), cv::Point(0, 70), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 255), 1);
  172. //cv::putText(image, "nums:" + std::to_string(speeds.size()), cv::Point(0, 110), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 255), 1);
  173. }
  174. else
  175. {
  176. _all_average_speed.push_back(-1); //多帧速度的集合
  177. _len_pixels.push_back(-1); //多帧,统计参与光流计算的像素点个数的集合
  178. _speed_variances.push_back(-1);
  179. }
  180. _count++;
  181. }
  182. _prevFrameGray = image;
  183. // 如果已累积处理了60帧数据
  184. if (_count >= 60)
  185. {
  186. // 移除-1
  187. std::vector<double> all_average_speed = RemoveNegativeOnes(_all_average_speed);
  188. std::vector<int> len_pixels = RemoveNegativeOnes(_len_pixels);
  189. std::vector<double> speed_variances = RemoveNegativeOnes(_speed_variances);
  190. // 60帧内没统计出光流速度(图像静止?)
  191. if (all_average_speed.empty())
  192. {
  193. return CardiacUsImgRecogStatus::FALSE;
  194. }
  195. all_average_speed = FunIQR(all_average_speed);
  196. // 剔除离群点后没有有效数据(数据无规律?)
  197. if (all_average_speed.empty())
  198. {
  199. return CardiacUsImgRecogStatus::FALSE;
  200. }
  201. double speed_meidian = Median(all_average_speed);// [..., ..., ...(存放多帧的平均速度)] 中位数
  202. // 计算中位数时是nan
  203. if (std::isnan(speed_meidian))
  204. {
  205. return CardiacUsImgRecogStatus::UNKNOW;
  206. }
  207. int pixels_num_sum = std::accumulate(len_pixels.begin(), len_pixels.end(), 0);
  208. double pixels_nums_average = static_cast<double>(pixels_num_sum) / len_pixels.size();//[..., ..., ...(存放多帧的参与像素点计算的个数)] 平均数
  209. double variance_sum = std::accumulate(speed_variances.begin(), speed_variances.end(), 0.0);
  210. double variance_average = variance_sum / speed_variances.size();//[..., ..., ...(存放多帧的速度的方差)] 平均数
  211. // 累积40帧已经做了一次判断,剔除一个历史数据,下次进新数据
  212. EraseFirstElementIfNotEmpty(_all_average_speed);
  213. EraseFirstElementIfNotEmpty(_len_pixels);
  214. EraseFirstElementIfNotEmpty(_speed_variances);
  215. _count--;
  216. //std::cout << "平均速度的中位数:" << speed_meidian << std::endl;
  217. //std::cout << "像素点个数的平均数:" << pixels_nums_average << std::endl;
  218. //std::cout << "速度方差的平均数:" << variance_average << std::endl;
  219. if (speed_meidian > 1.5 && pixels_nums_average >= 50 && variance_average > 0.65) //todo:阈值需调整
  220. {
  221. return CardiacUsImgRecogStatus::TRUE;
  222. }
  223. else
  224. {
  225. return CardiacUsImgRecogStatus::FALSE;
  226. }
  227. }
  228. return CardiacUsImgRecogStatus::UNKNOW; // 返回UNKNOWN直到累积40帧数据
  229. }
  230. //int main()
  231. //{
  232. //#pragma region test(创建帧列表)
  233. // // 加载图像、创建帧列表
  234. // std::vector<cv::Mat> frames;
  235. // std::string folderPath = "D:/Heart/2023-10-20/y/img/";
  236. // int numFrames = 60; // 文件数量
  237. //
  238. // for (int i = 0; i < numFrames; i++)
  239. // {
  240. // std::string filePath = folderPath + std::to_string(i) + ".jpg";
  241. // cv::Mat image = cv::imread(filePath);
  242. // if (!image.empty())
  243. // {
  244. // frames.push_back(image);
  245. // }
  246. // }
  247. //#pragma endregion
  248. //
  249. //#pragma region 读取文件夹下所有图像,缺点:存到vector中,不能按顺序存放,pass
  250. // //std::string folderPath = "D:/Heart/2023-10-20/q/img"; // 文件夹路径
  251. // //int imageCount = 0; // 计数器,用于跟踪已获取的图像数量
  252. //
  253. // //// 遍历文件夹中的所有图像文件,限制为前五十个
  254. // //for (const auto& entry : std::filesystem::directory_iterator(folderPath))
  255. // //{
  256. // // //if (imageCount >= 61)
  257. // // //{
  258. // // // break; // 达到了前五十个图像,退出循环
  259. // // //}
  260. //
  261. // // if (entry.is_regular_file() && entry.path().extension() == ".jpg")
  262. // // {
  263. // // cv::Mat image = cv::imread(entry.path().string());
  264. // // if (!image.empty())
  265. // // {
  266. // // frames.push_back(image);
  267. // // imageCount++; // 增加计数器
  268. // // }
  269. // // }
  270. // //}
  271. //#pragma endregion
  272. //
  273. // int step = 10;
  274. //
  275. // auto start = std::chrono::high_resolution_clock::now();
  276. //
  277. // StructImageInfo imageInfo;
  278. // imageInfo.Width = width;
  279. // imageInfo.Height = height;
  280. // imageInfo.Stride = width;
  281. // imageInfo.Channel = 3;
  282. //
  283. // bool isHeart = CheckIsCardiacUsImg(frames[0], step);
  284. //
  285. // auto end = std::chrono::high_resolution_clock::now();
  286. // // 计算时间差并以毫秒为单位
  287. // auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
  288. // // 输出执行时间
  289. // std::cout << "Execution time: " << duration.count() << "毫秒" << std::endl;
  290. // std::cout << "此区域为心脏?: " << isHeart << std::endl;
  291. //
  292. // return 0;
  293. //}