MainForm.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. // Copyright (c) 2012-2020 fo-dicom contributors.
  2. // Licensed under the Microsoft Public License (MS-PL).
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Windows.Forms;
  7. using Dicom.IO.Buffer;
  8. namespace Dicom.Compare
  9. {
  10. public partial class MainForm : Form
  11. {
  12. private static readonly Color None = Color.Transparent;
  13. private static readonly Color Green = Color.FromArgb(190, 240, 190);
  14. private static readonly Color Yellow = Color.FromArgb(255, 255, 217);
  15. private static readonly Color Red = Color.FromArgb(255, 200, 200);
  16. private static readonly Color Gray = Color.FromArgb(200, 200, 200);
  17. private DicomFile _file1;
  18. private DicomFile _file2;
  19. private int _level = 0;
  20. private string _indent = string.Empty;
  21. public MainForm()
  22. {
  23. InitializeComponent();
  24. }
  25. private void MainForm_Load(object sender, EventArgs e)
  26. {
  27. }
  28. public int Level
  29. {
  30. get => _level;
  31. set
  32. {
  33. _level = value;
  34. _indent = "".PadRight(_level * 4);
  35. }
  36. }
  37. private void OnClickSelect(object sender, EventArgs e)
  38. {
  39. DicomFile file1;
  40. while (true)
  41. {
  42. var ofd = new OpenFileDialog
  43. {
  44. Title = "Choose first DICOM file",
  45. Filter = "DICOM Files (*.dcm;*.dic)|*.dcm;*.dic|All Files (*.*)|*.*"
  46. };
  47. if (ofd.ShowDialog(this) == DialogResult.Cancel) return;
  48. try
  49. {
  50. file1 = DicomFile.Open(ofd.FileName);
  51. break;
  52. }
  53. catch (Exception ex)
  54. {
  55. MessageBox.Show(
  56. this,
  57. ex.Message,
  58. "Error opening DICOM file",
  59. MessageBoxButtons.OK,
  60. MessageBoxIcon.Error);
  61. }
  62. }
  63. DicomFile file2;
  64. while (true)
  65. {
  66. var ofd = new OpenFileDialog
  67. {
  68. Title = "Choose second DICOM file",
  69. Filter = "DICOM Files (*.dcm;*.dic)|*.dcm;*.dic|All Files (*.*)|*.*"
  70. };
  71. if (ofd.ShowDialog(this) == DialogResult.Cancel) return;
  72. try
  73. {
  74. file2 = DicomFile.Open(ofd.FileName);
  75. break;
  76. }
  77. catch (Exception ex)
  78. {
  79. MessageBox.Show(
  80. this,
  81. ex.Message,
  82. "Error opening DICOM file",
  83. MessageBoxButtons.OK,
  84. MessageBoxIcon.Error);
  85. }
  86. }
  87. _file1 = file1;
  88. _file2 = file2;
  89. lblFile1.Text = _file1.File.Name;
  90. lblFile2.Text = _file2.File.Name;
  91. CompareFiles();
  92. }
  93. private void CompareFiles()
  94. {
  95. Level = 0;
  96. try
  97. {
  98. lvFile1.BeginUpdate();
  99. lvFile2.BeginUpdate();
  100. lvFile1.Items.Clear();
  101. lvFile2.Items.Clear();
  102. CompareDatasets(_file1.FileMetaInfo, _file2.FileMetaInfo);
  103. CompareDatasets(_file1.Dataset, _file2.Dataset);
  104. OnSizeChanged(lvFile1, EventArgs.Empty);
  105. OnSizeChanged(lvFile2, EventArgs.Empty);
  106. }
  107. catch (Exception ex)
  108. {
  109. MessageBox.Show(
  110. this,
  111. ex.Message,
  112. "Error comparing DICOM files",
  113. MessageBoxButtons.OK,
  114. MessageBoxIcon.Error);
  115. }
  116. finally
  117. {
  118. lvFile1.EndUpdate();
  119. lvFile2.EndUpdate();
  120. }
  121. }
  122. private void CompareDatasets(DicomDataset d1, DicomDataset d2)
  123. {
  124. var e1 = new Queue<DicomItem>(d1 ?? new DicomDataset());
  125. var e2 = new Queue<DicomItem>(d2 ?? new DicomDataset());
  126. while (e1.Count > 0 || e2.Count > 0)
  127. {
  128. DicomItem i1 = null;
  129. if (e1.Count > 0) i1 = e1.Peek();
  130. DicomItem i2 = null;
  131. if (e2.Count > 0) i2 = e2.Peek();
  132. if (i1 != null && i2 != null)
  133. {
  134. if (i1.Tag.Group < i2.Tag.Group)
  135. {
  136. AddItem(i1, null);
  137. e1.Dequeue();
  138. continue;
  139. }
  140. if (i1.Tag.Group == i2.Tag.Group && i1.Tag.Element < i2.Tag.Element)
  141. {
  142. AddItem(i1, null);
  143. e1.Dequeue();
  144. continue;
  145. }
  146. }
  147. if (i2 != null && i1 != null)
  148. {
  149. if (i2.Tag.Group < i1.Tag.Group)
  150. {
  151. AddItem(null, i2);
  152. e2.Dequeue();
  153. continue;
  154. }
  155. if (i2.Tag.Group == i1.Tag.Group && i2.Tag.Element < i1.Tag.Element)
  156. {
  157. AddItem(null, i2);
  158. e2.Dequeue();
  159. continue;
  160. }
  161. }
  162. AddItem(i1, i2);
  163. if (i1 != null) e1.Dequeue();
  164. if (i2 != null) e2.Dequeue();
  165. }
  166. }
  167. private void CompareSequences(DicomSequence s1, DicomSequence s2)
  168. {
  169. if (s1 == null)
  170. {
  171. AddItem(s1, lvFile1, Gray);
  172. AddItem(s2, lvFile2, Green);
  173. }
  174. else if (s2 == null)
  175. {
  176. AddItem(s1, lvFile1, Green);
  177. AddItem(s2, lvFile2, Gray);
  178. }
  179. else
  180. {
  181. AddItem(s1, lvFile1, None);
  182. AddItem(s2, lvFile2, None);
  183. }
  184. Level++;
  185. int count = 0;
  186. if (s1 != null) count = s1.Items.Count;
  187. if (s2 != null && s2.Items.Count > count) count = s2.Items.Count;
  188. for (int i = 0; i < count; i++)
  189. {
  190. DicomDataset d1 = null;
  191. if (s1 != null && i < s1.Items.Count) d1 = s1.Items[i];
  192. DicomDataset d2 = null;
  193. if (s2 != null && i < s2.Items.Count) d2 = s2.Items[i];
  194. if (d1 == null)
  195. {
  196. AddItem(string.Empty, uint.MaxValue, string.Empty, lvFile1, Gray);
  197. AddItem(GetTagName(DicomTag.Item), uint.MaxValue, string.Empty, lvFile2, Green);
  198. }
  199. else if (d2 == null)
  200. {
  201. AddItem(GetTagName(DicomTag.Item), uint.MaxValue, string.Empty, lvFile1, Green);
  202. AddItem(string.Empty, uint.MaxValue, string.Empty, lvFile2, Gray);
  203. }
  204. else
  205. {
  206. AddItem(GetTagName(DicomTag.Item), uint.MaxValue, string.Empty, lvFile1, None);
  207. AddItem(GetTagName(DicomTag.Item), uint.MaxValue, string.Empty, lvFile2, None);
  208. }
  209. Level++;
  210. CompareDatasets(d1, d2);
  211. Level--;
  212. }
  213. Level--;
  214. }
  215. private void CompareFragments(DicomItem i1, DicomItem i2)
  216. {
  217. DicomFragmentSequence s1 = null;
  218. DicomFragmentSequence s2 = null;
  219. bool pixel = cbIgnorePixelData.Checked && i1.Tag == DicomTag.PixelData;
  220. if (i1 == null)
  221. {
  222. AddItem(i1, lvFile1, Gray);
  223. AddItem(i2, lvFile2, Green);
  224. s2 = i2 as DicomFragmentSequence;
  225. }
  226. else if (i2 == null)
  227. {
  228. AddItem(i1, lvFile1, Green);
  229. AddItem(i2, lvFile2, Gray);
  230. s1 = i1 as DicomFragmentSequence;
  231. }
  232. else if (!(i1 is DicomFragmentSequence))
  233. {
  234. AddItem(i1, lvFile1, pixel ? Yellow : Red);
  235. AddItem(i2, lvFile2, pixel ? Yellow : Red);
  236. s2 = i2 as DicomFragmentSequence;
  237. }
  238. else if (!(i2 is DicomFragmentSequence))
  239. {
  240. AddItem(i1, lvFile1, pixel ? Yellow : Red);
  241. AddItem(i2, lvFile2, pixel ? Yellow : Red);
  242. s1 = i1 as DicomFragmentSequence;
  243. }
  244. else
  245. {
  246. AddItem(i1, lvFile1, pixel ? Yellow : None);
  247. AddItem(i2, lvFile2, pixel ? Yellow : None);
  248. s1 = i1 as DicomFragmentSequence;
  249. s2 = i2 as DicomFragmentSequence;
  250. }
  251. Level++;
  252. if (s1 == null)
  253. {
  254. AddItem(string.Empty, uint.MaxValue, string.Empty, lvFile1, Gray);
  255. AddItem(
  256. _indent + "Offset Table",
  257. (uint)s2.OffsetTable.Count * 4,
  258. string.Format("@entries={0}", s2.OffsetTable.Count),
  259. lvFile2,
  260. pixel ? Yellow : Red);
  261. }
  262. else if (s2 == null)
  263. {
  264. AddItem(
  265. _indent + "Offset Table",
  266. (uint)s1.OffsetTable.Count * 4,
  267. string.Format("@entries={0}", s1.OffsetTable.Count),
  268. lvFile1,
  269. pixel ? Yellow : Red);
  270. AddItem(string.Empty, uint.MaxValue, string.Empty, lvFile2, Gray);
  271. }
  272. else
  273. {
  274. Color c = None;
  275. if (s1.OffsetTable.Count != s2.OffsetTable.Count) c = Red;
  276. else
  277. {
  278. for (int i = 0; i < s1.OffsetTable.Count; i++)
  279. {
  280. if (s1.OffsetTable[i] != s2.OffsetTable[i])
  281. {
  282. c = Red;
  283. break;
  284. }
  285. }
  286. }
  287. AddItem(
  288. _indent + "Offset Table",
  289. (uint)s2.OffsetTable.Count * 4,
  290. string.Format("@entries={0}", s1.OffsetTable.Count),
  291. lvFile1,
  292. pixel ? Yellow : c);
  293. AddItem(
  294. _indent + "Offset Table",
  295. (uint)s2.OffsetTable.Count * 4,
  296. string.Format("@entries={0}", s2.OffsetTable.Count),
  297. lvFile2,
  298. pixel ? Yellow : c);
  299. }
  300. int count = 0;
  301. if (s1 != null) count = s1.Fragments.Count;
  302. if (s2 != null && s2.Fragments.Count > count) count = s2.Fragments.Count;
  303. string name = _indent + "Fragment";
  304. for (int i = 0; i < count; i++)
  305. {
  306. IByteBuffer b1 = null;
  307. if (s1 != null && i < s1.Fragments.Count) b1 = s1.Fragments[i];
  308. IByteBuffer b2 = null;
  309. if (s2 != null && i < s2.Fragments.Count) b2 = s2.Fragments[i];
  310. if (b1 == null)
  311. {
  312. AddItem(string.Empty, uint.MaxValue, string.Empty, lvFile1, Gray);
  313. AddItem(name, (uint)b2.Size, string.Empty, lvFile2, pixel ? Yellow : Red);
  314. continue;
  315. }
  316. else if (b2 == null)
  317. {
  318. AddItem(name, (uint)b1.Size, string.Empty, lvFile1, pixel ? Yellow : Red);
  319. AddItem(string.Empty, uint.MaxValue, string.Empty, lvFile2, Gray);
  320. continue;
  321. }
  322. Color c = None;
  323. if (pixel) c = Yellow;
  324. else if (!Compare(b1.Data, b2.Data)) c = Red;
  325. AddItem(name, (uint)b1.Size, string.Empty, lvFile1, c);
  326. AddItem(name, (uint)b2.Size, string.Empty, lvFile2, c);
  327. }
  328. Level--;
  329. }
  330. private string GetTagName(DicomTag t)
  331. {
  332. return string.Format("{0}{1} {2}", _indent, t.ToString().ToUpper(), t.DictionaryEntry.Name);
  333. }
  334. private void AddItem(string t, uint l, string v, ListView lv, Color c)
  335. {
  336. var lvi = lv.Items.Add(t);
  337. lvi.SubItems.Add(!string.IsNullOrEmpty(t) ? "--" : string.Empty);
  338. if (l == uint.MaxValue) lvi.SubItems.Add(!string.IsNullOrEmpty(t) ? "-" : string.Empty);
  339. else lvi.SubItems.Add(l.ToString());
  340. lvi.SubItems.Add(v);
  341. lvi.UseItemStyleForSubItems = true;
  342. lvi.BackColor = c;
  343. }
  344. private void AddItem(DicomItem i, ListView lv, Color c)
  345. {
  346. ListViewItem lvi = null;
  347. if (i != null)
  348. {
  349. var tag = GetTagName(i.Tag);
  350. lvi = lv.Items.Add(tag);
  351. lvi.SubItems.Add(i.ValueRepresentation.Code);
  352. if (i is DicomElement)
  353. {
  354. var e = i as DicomElement;
  355. lvi.SubItems.Add(e.Length.ToString());
  356. string value = "<large value not displayed>";
  357. if (e.Length <= 2048) value = string.Join("\\", e.Get<string[]>());
  358. lvi.SubItems.Add(value);
  359. }
  360. else
  361. {
  362. lvi.SubItems.Add("-");
  363. lvi.SubItems.Add(string.Empty);
  364. }
  365. lvi.Tag = i;
  366. }
  367. else
  368. {
  369. lvi = lv.Items.Add(string.Empty);
  370. lvi.SubItems.Add(string.Empty);
  371. lvi.SubItems.Add(string.Empty);
  372. lvi.SubItems.Add(string.Empty);
  373. }
  374. lvi.UseItemStyleForSubItems = true;
  375. lvi.BackColor = c;
  376. }
  377. private void AddItem(DicomItem i1, DicomItem i2)
  378. {
  379. if (i1 is DicomSequence || i2 is DicomSequence)
  380. {
  381. CompareSequences(i1 as DicomSequence, i2 as DicomSequence);
  382. return;
  383. }
  384. if (i2 == null)
  385. {
  386. AddItem(i1, lvFile1, Green);
  387. AddItem(i2, lvFile2, Gray);
  388. return;
  389. }
  390. if (i1 == null)
  391. {
  392. AddItem(i1, lvFile1, Gray);
  393. AddItem(i2, lvFile2, Green);
  394. return;
  395. }
  396. if (i1 is DicomElement && i2 is DicomElement)
  397. {
  398. var e1 = i1 as DicomElement;
  399. var e2 = i2 as DicomElement;
  400. var c = None;
  401. if (!cbIgnoreVR.Checked && e1.ValueRepresentation != e2.ValueRepresentation) c = Red;
  402. else if (!Compare(e1.Buffer.Data, e2.Buffer.Data)) c = Red;
  403. if (cbIgnoreGroupLengths.Checked && e1.Tag.Element == 0x0000) c = Yellow;
  404. if (cbIgnoreUIDs.Checked && e1.ValueRepresentation == DicomVR.UI)
  405. {
  406. var uid = (i1 as DicomElement).Get<DicomUID>(0);
  407. if (uid != null && (uid.Type == DicomUidType.SOPInstance || uid.Type == DicomUidType.Unknown)) c = Yellow;
  408. }
  409. if (cbIgnorePixelData.Checked && i1.Tag == DicomTag.PixelData) c = Yellow;
  410. AddItem(i1, lvFile1, c);
  411. AddItem(i2, lvFile2, c);
  412. return;
  413. }
  414. if (i1 is DicomFragmentSequence || i2 is DicomFragmentSequence)
  415. {
  416. CompareFragments(i1, i2);
  417. return;
  418. }
  419. if (i1 is DicomElement || i2 is DicomElement)
  420. {
  421. AddItem(i1, lvFile1, Red);
  422. AddItem(i2, lvFile2, Red);
  423. return;
  424. }
  425. AddItem(i1, lvFile1, Yellow);
  426. AddItem(i2, lvFile2, Yellow);
  427. }
  428. private static bool Compare(byte[] b1, byte[] b2)
  429. {
  430. if (b1.Length != b2.Length) return false;
  431. for (int i = 0; i < b1.Length; i++)
  432. {
  433. if (b1[i] != b2[i]) return false;
  434. }
  435. return true;
  436. }
  437. private void OnScroll(object sender, ScrollEventArgs e)
  438. {
  439. if (sender == lvFile1)
  440. {
  441. int index = lvFile1.TopItem.Index;
  442. lvFile2.TopItem = lvFile2.Items[index];
  443. }
  444. else
  445. {
  446. int index = lvFile2.TopItem.Index;
  447. lvFile1.TopItem = lvFile1.Items[index];
  448. }
  449. }
  450. private void OnSelect(object sender, EventArgs e)
  451. {
  452. if (sender == lvFile1)
  453. {
  454. if (lvFile1.SelectedIndices.Count > 0)
  455. {
  456. int index = lvFile1.SelectedIndices[0];
  457. lvFile2.Items[index].Selected = true;
  458. }
  459. }
  460. else
  461. {
  462. if (lvFile2.SelectedIndices.Count > 0)
  463. {
  464. int index = lvFile2.SelectedIndices[0];
  465. lvFile1.Items[index].Selected = true;
  466. }
  467. }
  468. }
  469. private void OnMouseEnter(object sender, EventArgs e)
  470. {
  471. ((Control)sender).Focus();
  472. }
  473. private void OnSizeChanged(object sender, EventArgs e)
  474. {
  475. var lv = (ListViewEx)sender;
  476. var width = lv.Columns[0].Width + lv.Columns[1].Width + lv.Columns[2].Width;
  477. lv.Columns[3].Width = Math.Max(lv.ClientSize.Width - width, 440);
  478. }
  479. private void OnOptionChanged(object sender, EventArgs e)
  480. {
  481. int index = -1;
  482. if (lvFile1.TopItem != null) index = lvFile1.TopItem.Index;
  483. CompareFiles();
  484. if (index != -1 && index < lvFile1.Items.Count)
  485. {
  486. lvFile1.TopItem = lvFile1.Items[index];
  487. lvFile2.TopItem = lvFile2.Items[index];
  488. }
  489. }
  490. }
  491. }