NoiseDataFilter.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.IO;
  5. using System.Linq;
  6. namespace SHJX.Service.Common.Camera
  7. {
  8. /// <summary>
  9. /// 将队列中的前后两幅图像像素点进行相减,最大的10%和最小的10%视为噪点,其余值进行平均值计算
  10. /// </summary>
  11. public class NoiseDataFilter
  12. {
  13. public NoiseDataFilter()
  14. {
  15. this.Count = 2;
  16. this.lstImages = new List<List<Color>>();
  17. this.IsLog = false;
  18. this.LogWidth = 12;
  19. this.NoiseCnt = 10;
  20. }
  21. #region fields
  22. /// <summary>
  23. /// 队列中保留图像数,默认为2
  24. /// </summary>
  25. public int Count { get; set; }
  26. /// <summary>
  27. /// 过滤噪点数,单向
  28. /// </summary>
  29. public int NoiseCnt { get; set; }
  30. /// <summary>
  31. /// 是否写日志
  32. /// </summary>
  33. public bool IsLog { get; set; }
  34. /// <summary>
  35. /// 日志写入流
  36. /// </summary>
  37. public StreamWriter swLog { get; set; }
  38. /// <summary>
  39. /// 日志数据一行个数
  40. /// </summary>
  41. public int LogWidth { get; set; }
  42. /// <summary>
  43. /// 用于比较的图像像素队列
  44. /// </summary>
  45. protected List<List<Color>> lstImages { get; set; }
  46. #endregion
  47. #region methods
  48. /// <summary>
  49. /// 加入一幅图像到队列,不做处理
  50. /// </summary>
  51. /// <param name="newImage"></param>
  52. public void AddImage(List<Color> newImage)
  53. {
  54. this.lstImages.Add(newImage);
  55. while (this.lstImages.Count > this.Count)
  56. {
  57. this.lstImages.RemoveAt(0);
  58. }
  59. }
  60. /// <summary>
  61. /// 对图像进行噪点过滤,然后计算rgb总值
  62. /// </summary>
  63. /// <param name="newImage"></param>
  64. /// <param name="sumR"></param>
  65. /// <param name="sumG"></param>
  66. /// <param name="sumB"></param>
  67. public void CalcImage(List<Color> newImage, ref int sumR, ref int sumG, ref int sumB)
  68. {
  69. this.AddImage(newImage);
  70. this.calcAvg(ref sumR, ref sumG, ref sumB);
  71. }
  72. /// <summary>
  73. /// 清除队列数据
  74. /// </summary>
  75. public void Clear()
  76. {
  77. if (this.lstImages != null)
  78. {
  79. this.lstImages.Clear();
  80. }
  81. }
  82. #endregion
  83. #region protected methods
  84. /// <summary>
  85. /// 计算后图减前图的过滤图的总值
  86. /// </summary>
  87. /// <param name="sumR"></param>
  88. /// <param name="sumG"></param>
  89. /// <param name="sumB"></param>
  90. protected void calcAvg1(ref int sumR, ref int sumG, ref int sumB)
  91. {
  92. if (this.lstImages == null)
  93. {
  94. throw new Exception("队列没有数据");
  95. }
  96. if (this.lstImages.Count >= 2)
  97. {
  98. // 计算滤噪后的总值
  99. List<Color> img1 = this.lstImages[this.lstImages.Count - 2]; // 上一幅
  100. List<Color> img2 = this.lstImages[this.lstImages.Count - 1]; // 当前幅
  101. #region 计算后图减前图的差值
  102. string strr = "R:\r\n", strg = "G:\r\n", strb = "B:\r\n";
  103. List<IntRGB> imgsub = new List<IntRGB>();
  104. IntRGB val;
  105. for (int i = 0; i < img2.Count; i++)
  106. {
  107. val = new IntRGB(
  108. img2[i].R - img1[i].R,
  109. img2[i].G - img1[i].G,
  110. img2[i].B - img1[i].B);
  111. imgsub.Add(val);
  112. if (this.IsLog)
  113. {
  114. strr = string.Format("{0}{1},", strr, val.R.ToString().PadLeft(3, ' '));
  115. strg = string.Format("{0}{1},", strg, val.G.ToString().PadLeft(3, ' '));
  116. strb = string.Format("{0}{1},", strb, val.B.ToString().PadLeft(3, ' '));
  117. if (i % this.LogWidth == 11)
  118. {
  119. strr = string.Format("{0}\r\n", strr);
  120. strg = string.Format("{0}\r\n", strg);
  121. strb = string.Format("{0}\r\n", strb);
  122. }
  123. }
  124. }
  125. if (this.IsLog)
  126. {
  127. //strr = string.Format("{0}\r\n", strr);
  128. //strg = string.Format("{0}\r\n", strg);
  129. //strb = string.Format("{0}\r\n", strb);
  130. this.swLog.WriteLine("相减数据:");
  131. this.swLog.WriteLine(strr);
  132. this.swLog.WriteLine(strg);
  133. this.swLog.WriteLine(strb);
  134. this.swLog.WriteLine();
  135. }
  136. #endregion
  137. #region 统计差值,以G值为准
  138. Dictionary<int, int> dicimg = new Dictionary<int, int>();
  139. foreach (IntRGB subval in imgsub)
  140. {
  141. if (dicimg.Keys.Contains(subval.G))
  142. {
  143. dicimg[subval.G] += 1;
  144. }
  145. else
  146. {
  147. dicimg.Add(subval.G, 1);
  148. }
  149. }
  150. List<KeyValuePair<int, int>> lstsub = dicimg.OrderByDescending(c => c.Key).ToList();
  151. #endregion
  152. #region 计算阈值参数
  153. NoiseParam npm = new NoiseParam(this.NoiseCnt);
  154. npm.Threhold = img2.Count / 10;
  155. int tmp = 0; // 计算上阈值
  156. for (int i = 0; i < lstsub.Count; i++)
  157. {
  158. tmp += lstsub[i].Value;
  159. if (tmp < npm.Threhold)
  160. {
  161. if (this.IsLog) { this.swLog.WriteLine("滤除噪点+:" + lstsub[i].Key.ToString() + "[" + lstsub[i].Value.ToString() + "点]"); }
  162. continue; // 未够数,继续下一组
  163. }
  164. else if (tmp == npm.Threhold)
  165. {
  166. // 正好够数,记录阈值,退出循环
  167. npm.Upper = lstsub[i].Key;
  168. npm.UpCnt = lstsub[i].Value; // 该阈值需消除的点数
  169. if (this.IsLog) { this.swLog.WriteLine("滤除噪点+:" + lstsub[i].Key.ToString() + "[" + npm.UpCnt.ToString() + "点]"); }
  170. break;
  171. }
  172. else
  173. {
  174. // 过数,计算阈值,退出循环
  175. npm.Upper = lstsub[i].Key;
  176. npm.UpCnt = lstsub[i].Value - (tmp - npm.Threhold);
  177. if (this.IsLog) { this.swLog.WriteLine("滤除噪点+:" + lstsub[i].Key.ToString() + "[" + npm.UpCnt.ToString() + "点]"); }
  178. break;
  179. }
  180. }
  181. tmp = 0; // 计算下阈值
  182. for (int i = lstsub.Count - 1; i >= 0; i--)
  183. {
  184. tmp += lstsub[i].Value;
  185. if (tmp < npm.Threhold)
  186. {
  187. if (this.IsLog) { this.swLog.WriteLine("滤除噪点-:" + lstsub[i].Key.ToString() + "[" + lstsub[i].Value.ToString() + "点]"); }
  188. continue; // 未够数,继续下一组
  189. }
  190. else if (tmp == npm.Threhold)
  191. {
  192. // 正好够数,记录阈值,退出循环
  193. npm.Lower = lstsub[i].Key;
  194. npm.LowCnt = lstsub[i].Value; // 该阈值需消除的点数
  195. if (this.IsLog) { this.swLog.WriteLine("滤除噪点-:" + lstsub[i].Key.ToString() + "[" + npm.LowCnt.ToString() + "点]"); }
  196. break;
  197. }
  198. else
  199. {
  200. // 过数,计算阈值,退出循环
  201. npm.Lower = lstsub[i].Key;
  202. npm.LowCnt = lstsub[i].Value - (tmp - npm.Threhold);
  203. if (this.IsLog) { this.swLog.WriteLine("滤除噪点-:" + lstsub[i].Key.ToString() + "[" + npm.LowCnt.ToString() + "点]"); }
  204. break;
  205. }
  206. }
  207. #endregion
  208. #region 滤除噪点后,计算总值
  209. for (int i = 0; i < img2.Count; i++)
  210. {
  211. if (imgsub[i].G > npm.Upper || imgsub[i].G < npm.Lower)
  212. {
  213. continue; // 噪点,滤去
  214. }
  215. else if (imgsub[i].G == npm.Upper && npm.UpCnt > 0)
  216. {
  217. // 部分噪点,滤去部分
  218. npm.UpCnt--;
  219. continue;
  220. }
  221. else if (imgsub[i].G == npm.Lower && npm.LowCnt > 0)
  222. {
  223. // 部分噪点,滤去部分
  224. npm.LowCnt--;
  225. continue;
  226. }
  227. else
  228. {
  229. // 非噪点,计算总值
  230. sumR += img2[i].R;
  231. sumG += img2[i].G;
  232. sumB += img2[i].B;
  233. }
  234. }
  235. if (this.IsLog)
  236. {
  237. tmp = img2.Count - npm.Threhold * 2;
  238. strr = string.Format("总值:(R, G, B)({0}, {1}, {2}) 数据个数:{3}", sumR, sumG, sumB, tmp);
  239. this.swLog.WriteLine(strr);
  240. strr = string.Format("均值:(R, G, B)({0}, {1}, {2})\r\n", (sumR * 1.0 / tmp).ToString("F2"), (sumG * 1.0 / tmp).ToString("F2"), (sumB * 1.0 / tmp).ToString("F2"));
  241. this.swLog.WriteLine(strr);
  242. this.swLog.WriteLine();
  243. }
  244. #endregion
  245. }
  246. else
  247. {
  248. #region 仅一图,无法去噪,直接计算总值
  249. List<Color> img2 = this.lstImages[this.lstImages.Count - 1]; // 上一幅
  250. for (int i = 0; i < img2.Count; i++)
  251. {
  252. sumR += img2[i].R;
  253. sumG += img2[i].G;
  254. sumB += img2[i].B;
  255. }
  256. if (this.IsLog)
  257. {
  258. int tmp = img2.Count;
  259. string strr = string.Format("总值:(R, G, B)({0}, {1}, {2}) 数据个数:{3}", sumR, sumG, sumB, tmp);
  260. this.swLog.WriteLine(strr);
  261. strr = string.Format("均值:(R, G, B)({0}, {1}, {2})\r\n", (sumR * 1.0 / tmp).ToString("F2"), (sumG * 1.0 / tmp).ToString("F2"), (sumB * 1.0 / tmp).ToString("F2"));
  262. this.swLog.WriteLine(strr);
  263. this.swLog.WriteLine();
  264. }
  265. #endregion
  266. }
  267. }
  268. protected void calcAvg(ref int sumR, ref int sumG, ref int sumB)
  269. {
  270. if (this.lstImages == null)
  271. {
  272. throw new Exception("队列没有数据");
  273. }
  274. if (this.lstImages.Count >= 2)
  275. {
  276. // 计算滤噪后的总值
  277. List<Color> img1 = this.lstImages[this.lstImages.Count - 2]; // 上一幅
  278. List<Color> img2 = this.lstImages[this.lstImages.Count - 1]; // 当前幅
  279. #region 计算后图减前图的差值
  280. string strr = "R:\r\n", strg = "G:\r\n", strb = "B:\r\n";
  281. List<IntRGB> imgsub = new List<IntRGB>();
  282. IntRGB val;
  283. for (int i = 0; i < img2.Count; i++)
  284. {
  285. val = new IntRGB(
  286. img2[i].R - img1[i].R,
  287. img2[i].G - img1[i].G,
  288. img2[i].B - img1[i].B);
  289. imgsub.Add(val);
  290. if (this.IsLog)
  291. {
  292. strr = string.Format("{0}{1},", strr, val.R.ToString().PadLeft(3, ' '));
  293. strg = string.Format("{0}{1},", strg, val.G.ToString().PadLeft(3, ' '));
  294. strb = string.Format("{0}{1},", strb, val.B.ToString().PadLeft(3, ' '));
  295. if (i % this.LogWidth == this.LogWidth - 1)
  296. {
  297. strr = string.Format("{0}\r\n", strr);
  298. strg = string.Format("{0}\r\n", strg);
  299. strb = string.Format("{0}\r\n", strb);
  300. }
  301. }
  302. }
  303. if (this.IsLog)
  304. {
  305. //strr = string.Format("{0}\r\n", strr);
  306. //strg = string.Format("{0}\r\n", strg);
  307. //strb = string.Format("{0}\r\n", strb);
  308. this.swLog.WriteLine("相减数据:");
  309. this.swLog.WriteLine(strr);
  310. this.swLog.WriteLine(strg);
  311. this.swLog.WriteLine(strb);
  312. this.swLog.WriteLine();
  313. }
  314. #endregion
  315. #region 统计差值,以G值为准
  316. Dictionary<int, int> dicimg = new Dictionary<int, int>();
  317. foreach (IntRGB subval in imgsub)
  318. {
  319. if (dicimg.Keys.Contains(subval.G))
  320. {
  321. dicimg[subval.G] += 1;
  322. }
  323. else
  324. {
  325. dicimg.Add(subval.G, 1);
  326. }
  327. }
  328. List<KeyValuePair<int, int>> lstsub = dicimg.OrderByDescending(c => c.Key).ToList();
  329. #endregion
  330. #region 计算阈值参数
  331. NoiseParam npm = new NoiseParam(this.NoiseCnt);
  332. npm.Threhold = img2.Count / 10;
  333. int tmp = 0; // 计算上阈值
  334. for (int i = 0; i < lstsub.Count; i++)
  335. {
  336. tmp += lstsub[i].Value;
  337. if (tmp < npm.Threhold)
  338. {
  339. if (this.IsLog) { this.swLog.WriteLine("滤除噪点+:" + lstsub[i].Key.ToString() + "[" + lstsub[i].Value.ToString() + "点]"); }
  340. continue; // 未够数,继续下一组
  341. }
  342. else if (tmp == npm.Threhold)
  343. {
  344. // 正好够数,记录阈值,退出循环
  345. npm.Upper = lstsub[i].Key;
  346. npm.UpCnt = lstsub[i].Value; // 该阈值需消除的点数
  347. if (this.IsLog) { this.swLog.WriteLine("滤除噪点+:" + lstsub[i].Key.ToString() + "[" + npm.UpCnt.ToString() + "点]"); }
  348. break;
  349. }
  350. else
  351. {
  352. // 过数,计算阈值,退出循环
  353. npm.Upper = lstsub[i].Key;
  354. npm.UpCnt = lstsub[i].Value - (tmp - npm.Threhold);
  355. if (this.IsLog) { this.swLog.WriteLine("滤除噪点+:" + lstsub[i].Key.ToString() + "[" + npm.UpCnt.ToString() + "点]"); }
  356. break;
  357. }
  358. }
  359. tmp = 0; // 计算下阈值
  360. for (int i = lstsub.Count - 1; i >= 0; i--)
  361. {
  362. tmp += lstsub[i].Value;
  363. if (tmp < npm.Threhold)
  364. {
  365. if (this.IsLog) { this.swLog.WriteLine("滤除噪点-:" + lstsub[i].Key.ToString() + "[" + lstsub[i].Value.ToString() + "点]"); }
  366. continue; // 未够数,继续下一组
  367. }
  368. else if (tmp == npm.Threhold)
  369. {
  370. // 正好够数,记录阈值,退出循环
  371. npm.Lower = lstsub[i].Key;
  372. npm.LowCnt = lstsub[i].Value; // 该阈值需消除的点数
  373. if (this.IsLog) { this.swLog.WriteLine("滤除噪点-:" + lstsub[i].Key.ToString() + "[" + npm.LowCnt.ToString() + "点]"); }
  374. break;
  375. }
  376. else
  377. {
  378. // 过数,计算阈值,退出循环
  379. npm.Lower = lstsub[i].Key;
  380. npm.LowCnt = lstsub[i].Value - (tmp - npm.Threhold);
  381. if (this.IsLog) { this.swLog.WriteLine("滤除噪点-:" + lstsub[i].Key.ToString() + "[" + npm.LowCnt.ToString() + "点]"); }
  382. break;
  383. }
  384. }
  385. #endregion
  386. #region 滤除噪点后,计算总值
  387. double tmpR = 0.0, tmpG = 0.0, tmpB = 0.0;
  388. double tmpu = (dicimg[npm.Upper] - npm.UpCnt) * 1.0 / dicimg[npm.Upper]; // 部分过滤点保留的比率
  389. if (this.IsLog)
  390. {
  391. this.swLog.WriteLine(
  392. string.Format("噪点+:{0}[{1}/{2}]={3}", npm.Upper.ToString(), npm.UpCnt, dicimg[npm.Upper], tmpu.ToString("F2")));
  393. }
  394. double tmpd = (dicimg[npm.Lower] - npm.LowCnt) * 1.0 / dicimg[npm.Lower]; // 部分过滤点保留的比率
  395. if (this.IsLog)
  396. {
  397. this.swLog.WriteLine(
  398. string.Format("噪点-:{0}[{1}/{2}]={3}", npm.Lower.ToString(), npm.LowCnt, dicimg[npm.Lower], tmpd.ToString("F2")));
  399. }
  400. for (int i = 0; i < img2.Count; i++)
  401. {
  402. if (imgsub[i].G > npm.Upper || imgsub[i].G < npm.Lower)
  403. {
  404. continue; // 噪点,滤去
  405. }
  406. else if (imgsub[i].G == npm.Upper && npm.UpCnt > 0)
  407. {
  408. // 部分噪点,滤去部分
  409. tmpR += img2[i].R * tmpu;
  410. tmpG += img2[i].G * tmpu;
  411. tmpB += img2[i].B * tmpu;
  412. continue;
  413. }
  414. else if (imgsub[i].G == npm.Lower && npm.LowCnt > 0)
  415. {
  416. // 部分噪点,滤去部分
  417. tmpR += img2[i].R * tmpd;
  418. tmpG += img2[i].G * tmpd;
  419. tmpB += img2[i].B * tmpd;
  420. continue;
  421. }
  422. else
  423. {
  424. // 非噪点,计算总值
  425. tmpR += img2[i].R;
  426. tmpG += img2[i].G;
  427. tmpB += img2[i].B;
  428. }
  429. }
  430. sumR = Convert.ToInt32(tmpR);
  431. sumG = Convert.ToInt32(tmpG);
  432. sumB = Convert.ToInt32(tmpB);
  433. if (this.IsLog)
  434. {
  435. tmp = img2.Count - npm.Threhold * 2;
  436. strr = string.Format("处理总值:(R, G, B)({0}, {1}, {2}), 数据个数:{3}, ", sumR, sumG, sumB, tmp)
  437. + string.Format("均值:(R, G, B)({0}, {1}, {2})", (sumR * 1.0 / tmp).ToString("F2"), (sumG * 1.0 / tmp).ToString("F2"), (sumB * 1.0 / tmp).ToString("F2"));
  438. this.swLog.WriteLine(strr);
  439. //this.swLog.WriteLine();
  440. }
  441. #endregion
  442. }
  443. else
  444. {
  445. #region 仅一图,无法去噪,直接计算总值
  446. List<Color> img2 = this.lstImages[this.lstImages.Count - 1]; // 上一幅
  447. for (int i = 0; i < img2.Count; i++)
  448. {
  449. sumR += img2[i].R;
  450. sumG += img2[i].G;
  451. sumB += img2[i].B;
  452. }
  453. if (this.IsLog)
  454. {
  455. int tmp = img2.Count;
  456. string strr = string.Format("处理总值:(R, G, B)({0}, {1}, {2}), 数据个数:{3}, ", sumR, sumG, sumB, tmp)
  457. + string.Format("均值:(R, G, B)({0}, {1}, {2})", (sumR * 1.0 / tmp).ToString("F2"), (sumG * 1.0 / tmp).ToString("F2"), (sumB * 1.0 / tmp).ToString("F2"));
  458. this.swLog.WriteLine(strr);
  459. //this.swLog.WriteLine();
  460. }
  461. #endregion
  462. }
  463. }
  464. #endregion
  465. }
  466. /// <summary>
  467. /// 用int类型保存RGB三个值
  468. /// </summary>
  469. public class IntRGB
  470. {
  471. #region properties
  472. public int R { get; set; }
  473. public int G { get; set; }
  474. public int B { get; set; }
  475. #endregion
  476. public IntRGB()
  477. {
  478. this.R = 0;
  479. this.G = 0;
  480. this.B = 0;
  481. }
  482. public IntRGB(int r, int g, int b)
  483. {
  484. this.R = r;
  485. this.G = g;
  486. this.B = b;
  487. }
  488. }
  489. /// <summary>
  490. /// 噪点相关参数
  491. /// </summary>
  492. public class NoiseParam
  493. {
  494. /// <summary>
  495. /// 前后两图相减法,去除上下噪点
  496. /// </summary>
  497. /// <param name="threhold">上下各自噪点数</param>
  498. public NoiseParam(int threhold)
  499. {
  500. this.Threhold = threhold;
  501. this.Upper = 0;
  502. this.UpCnt = 0;
  503. this.Lower = 0;
  504. this.LowCnt = 0;
  505. }
  506. #region
  507. /// <summary>
  508. /// 噪点个数
  509. /// </summary>
  510. public int Threhold { get; set; }
  511. /// <summary>
  512. /// 噪点上阈值
  513. /// </summary>
  514. public int Upper { get; set; }
  515. /// <summary>
  516. /// 上阈值噪点数个数
  517. /// </summary>
  518. public int UpCnt { get; set; }
  519. /// <summary>
  520. /// 噪点下阈值
  521. /// </summary>
  522. public int Lower { get; set; }
  523. /// <summary>
  524. /// 下阈值噪点个数
  525. /// </summary>
  526. public int LowCnt { get; set; }
  527. #endregion
  528. #region
  529. #endregion
  530. }
  531. }