You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
18 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Data;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using System.Reflection;
  9. using System.Diagnostics;
  10. using Emgu.CV;
  11. using Emgu.Util;
  12. using Emgu.CV.Structure;
  13. namespace Emgu.CV.UI
  14. {
  15. /// <summary>
  16. /// An image box is a user control that is similar to picture box, but display Emgu CV IImage and provides enhenced functionalities.
  17. /// </summary>
  18. public partial class ImageBox : PictureBox
  19. {
  20. #region private fileds
  21. private IImage _image;
  22. private IImage _displayedImage;
  23. private PropertyDialog _propertyDlg;
  24. private FunctionalModeOption _functionalMode = FunctionalModeOption.Everything;
  25. /// <summary>
  26. /// A cache to store the ToolStripMenuItems for different types of images
  27. /// </summary>
  28. private static readonly Dictionary<Type, ToolStripMenuItem[]> _typeToToolStripMenuItemsDictionary = new Dictionary<Type, ToolStripMenuItem[]>();
  29. private Stack<Operation> _operationStack;
  30. //private double _zoomLevel = 1.0;
  31. /// <summary>
  32. /// timer used for caculating the frame rate
  33. /// </summary>
  34. private DateTime _timerStartTime;
  35. /// <summary>
  36. /// one of the parameters used for caculating the frame rate
  37. /// </summary>
  38. private int _imageReceivedSinceCounterStart;
  39. #endregion
  40. /// <summary>
  41. /// Create a ImageBox
  42. /// </summary>
  43. public ImageBox()
  44. : base()
  45. {
  46. InitializeComponent();
  47. _operationStack = new Stack<Operation>();
  48. }
  49. #region properties
  50. /// <summary>
  51. /// Get or set the functional mode for the ImageBox
  52. /// </summary>
  53. [Bindable(false)]
  54. [Category("Design")]
  55. [DefaultValue(Emgu.CV.UI.ImageBox.FunctionalModeOption.Everything)]
  56. public FunctionalModeOption FunctionalMode
  57. {
  58. get { return _functionalMode; }
  59. set
  60. {
  61. //hide all menu items
  62. foreach (ToolStripMenuItem mi in contextMenuStrip1.Items)
  63. mi.Visible = false;
  64. if (value == FunctionalModeOption.Everything)
  65. foreach (ToolStripMenuItem mi in contextMenuStrip1.Items)
  66. mi.Visible = true;
  67. _functionalMode = value;
  68. }
  69. }
  70. private bool EnablePropertyPanel
  71. {
  72. get
  73. {
  74. return _propertyDlg != null;
  75. }
  76. set
  77. {
  78. if (value)
  79. { //this is a call to enable the property dlg
  80. if (_propertyDlg == null)
  81. _propertyDlg = new PropertyDialog(this);
  82. _propertyDlg.Show();
  83. _propertyDlg.FormClosed +=
  84. delegate(object sender, FormClosedEventArgs e)
  85. {
  86. _propertyDlg = null;
  87. };
  88. ImagePropertyPanel.SetOperationStack(_operationStack);
  89. // reset the image such that the property is updated
  90. Image = Image;
  91. }
  92. else
  93. {
  94. if (_propertyDlg != null)
  95. {
  96. _propertyDlg.Focus();
  97. }
  98. }
  99. }
  100. }
  101. /// <summary>
  102. /// Set the image for this image box
  103. /// </summary>
  104. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  105. public new IImage Image
  106. {
  107. get
  108. {
  109. return _image;
  110. }
  111. set
  112. {
  113. if (InvokeRequired)
  114. {
  115. this.Invoke(new MethodInvoker(delegate() { Image = value; }));
  116. }
  117. else
  118. {
  119. _image = value;
  120. IImage imageToBeDisplayed = _image;
  121. if (imageToBeDisplayed != null)
  122. {
  123. #region perform operations on _operationStack if there is any
  124. if (_operationStack.Count > 0)
  125. {
  126. bool isCloned = false;
  127. Operation[] ops = _operationStack.ToArray();
  128. System.Array.Reverse(ops);
  129. foreach (Operation operation in ops)
  130. {
  131. if (operation.Method.ReturnType == typeof(void))
  132. { //if this is an inplace operation
  133. if (!isCloned)
  134. { //make a clone if not already done so
  135. imageToBeDisplayed = _image.Clone() as IImage;
  136. isCloned = true;
  137. }
  138. //execute the inplace operation
  139. operation.InvokeMethod(imageToBeDisplayed);
  140. }
  141. else if (operation.Method.ReturnType.GetInterface("IImage") != null)
  142. { //if this operation has return value
  143. IImage tmp = null;
  144. if (isCloned == true) //if intermediate image exist, keep a reference of it
  145. tmp = imageToBeDisplayed;
  146. isCloned = true;
  147. imageToBeDisplayed = operation.InvokeMethod(imageToBeDisplayed) as IImage;
  148. if (tmp != null) //dispose the intermediate image
  149. tmp.Dispose();
  150. }
  151. else
  152. {
  153. throw new System.NotImplementedException(string.Format("Return type of {0} is not implemented.", operation.Method.ReturnType));
  154. }
  155. }
  156. }
  157. #endregion
  158. DisplayedImage = imageToBeDisplayed;
  159. }
  160. operationsToolStripMenuItem.Enabled = (_image != null);
  161. }
  162. }
  163. }
  164. /// <summary>
  165. /// The image that is being displayed. It's the Image following by some user defined image operation
  166. /// </summary>
  167. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  168. public IImage DisplayedImage
  169. {
  170. get
  171. {
  172. return _displayedImage;
  173. }
  174. set
  175. {
  176. _displayedImage = value;
  177. if (_displayedImage != null)
  178. {
  179. if (_functionalMode != FunctionalModeOption.Minimum)
  180. BuildOperationMenuItem(_displayedImage);
  181. //release the old Bitmap Image
  182. if (base.Image != null)
  183. base.Image.Dispose();
  184. base.Image = _displayedImage.Bitmap;
  185. if (EnablePropertyPanel)
  186. {
  187. ImagePropertyPanel.ImageSize = _displayedImage.Size;
  188. ImagePropertyPanel.TypeOfColor = Reflection.ReflectIImage.GetTypeOfColor(_displayedImage);
  189. ImagePropertyPanel.TypeOfDepth = Reflection.ReflectIImage.GetTypeOfDepth(_displayedImage);
  190. #region calculate the frame rate
  191. TimeSpan ts = DateTime.Now.Subtract(_timerStartTime);
  192. if (ts.TotalSeconds > 1)
  193. {
  194. ImagePropertyPanel.FramesPerSecondText = _imageReceivedSinceCounterStart;
  195. //reset the counter
  196. _timerStartTime = DateTime.Now;
  197. _imageReceivedSinceCounterStart = 0;
  198. }
  199. else
  200. {
  201. _imageReceivedSinceCounterStart++;
  202. }
  203. #endregion
  204. }
  205. }
  206. }
  207. }
  208. #endregion
  209. /// <summary>
  210. /// Push the specific operation onto the stack
  211. /// </summary>
  212. /// <param name="operation">The operation to be pushed onto the stack</param>
  213. public void PushOperation(Operation operation)
  214. {
  215. _operationStack.Push(operation);
  216. ImageProperty panel = ImagePropertyPanel;
  217. if (panel != null)
  218. panel.SetOperationStack(_operationStack);
  219. try
  220. {
  221. Image = Image;
  222. }
  223. catch
  224. { //if pushing the operation generate exceptions
  225. _operationStack.Pop(); //remove the operation from the stack
  226. if (panel != null)
  227. panel.SetOperationStack(_operationStack); //update the operation stack
  228. Image = Image; //update the image
  229. throw; //rethrow the exception
  230. }
  231. }
  232. /// <summary>
  233. /// Remove all the operations from the stack
  234. /// </summary>
  235. public void ClearOperation()
  236. {
  237. _operationStack.Clear();
  238. ImageProperty panel = ImagePropertyPanel;
  239. if (panel != null) panel.SetOperationStack(_operationStack);
  240. Image = Image;
  241. }
  242. /// <summary>
  243. /// Remove the last added operation from the stack
  244. /// </summary>
  245. public void PopOperation()
  246. {
  247. if (_operationStack.Count > 0)
  248. {
  249. _operationStack.Pop();
  250. ImageProperty panel = ImagePropertyPanel;
  251. if (panel != null) panel.SetOperationStack(_operationStack);
  252. Image = Image;
  253. }
  254. }
  255. private ToolStripMenuItem[] BuildOperationTree(IEnumerable<KeyValuePair<string, MethodInfo>> catelogMiPairList)
  256. {
  257. List<ToolStripMenuItem> res = new List<ToolStripMenuItem>();
  258. SortedDictionary<String, List<KeyValuePair<String, MethodInfo>>> catelogDic = new SortedDictionary<string, List<KeyValuePair<String, MethodInfo>>>();
  259. SortedDictionary<String, MethodInfo> operationItem = new SortedDictionary<string, MethodInfo>();
  260. foreach (KeyValuePair<string, MethodInfo> pair in catelogMiPairList)
  261. {
  262. if (String.IsNullOrEmpty(pair.Key))
  263. { //this is an operation
  264. operationItem.Add(pair.Value.ToString(), pair.Value);
  265. }
  266. else
  267. { //this is a category
  268. int index = pair.Key.IndexOf('|');
  269. String category;
  270. String subcategory;
  271. if (index == -1)
  272. { //There is no sub category
  273. category = pair.Key;
  274. subcategory = string.Empty;
  275. }
  276. else
  277. { //There are sub categories
  278. category = pair.Key.Substring(0, index);
  279. subcategory = pair.Key.Substring(index + 1, pair.Key.Length - index - 1);
  280. }
  281. if (!catelogDic.ContainsKey(category))
  282. catelogDic.Add(category, new List<KeyValuePair<String, MethodInfo>>());
  283. catelogDic[category].Add(new KeyValuePair<String, MethodInfo>(subcategory, pair.Value));
  284. }
  285. }
  286. #region Add catelogs to the menu
  287. foreach (String catelog in catelogDic.Keys)
  288. {
  289. ToolStripMenuItem catelogMenuItem = new ToolStripMenuItem();
  290. catelogMenuItem.Text = catelog;
  291. catelogMenuItem.DropDownItems.AddRange(BuildOperationTree(catelogDic[catelog]));
  292. res.Add(catelogMenuItem);
  293. }
  294. #endregion
  295. #region Add Methods to the menu
  296. foreach (MethodInfo mi in operationItem.Values)
  297. {
  298. ToolStripMenuItem operationMenuItem = new ToolStripMenuItem();
  299. operationMenuItem.Size = new System.Drawing.Size(152, 22);
  300. Type[] genericArgs = mi.GetGenericArguments();
  301. String genericArgString = genericArgs.Length > 0 ?
  302. String.Format(
  303. "<{0}>",
  304. String.Join(",", Array.ConvertAll<Type, String>(
  305. genericArgs,
  306. System.Convert.ToString)))
  307. : string.Empty;
  308. operationMenuItem.Text = String.Format(
  309. "{0}{1}({2})",
  310. mi.Name,
  311. genericArgString,
  312. String.Join(",", System.Array.ConvertAll<ParameterInfo, String>(mi.GetParameters(), delegate(ParameterInfo pi) { return pi.Name; })));
  313. //This is necessary to handle delegate with a loop
  314. //Cause me lots of headache before reading the article on
  315. //http://decav.com/blogs/andre/archive/2007/11/18/wtf-quot-problems-quot-with-anonymous-delegates-linq-lambdas-and-quot-foreach-quot-or-quot-for-quot-loops.aspx
  316. //I wishes MSFT handle this better
  317. MethodInfo methodInfoRef = mi;
  318. operationMenuItem.Click += delegate(Object o, EventArgs e)
  319. {
  320. Object[] paramList = null;
  321. while (true)
  322. {
  323. //Get the parameters for the method
  324. //this pop up an input dialog and ask for user input
  325. paramList = ParameterInputDialog.GetParams(methodInfoRef, paramList);
  326. if (paramList == null) break; //user click cancel on the input dialog
  327. //create an operation from the specific methodInfo and parameterlist
  328. Operation operation = new Operation(methodInfoRef, paramList);
  329. try
  330. {
  331. PushOperation(operation);
  332. break;
  333. }
  334. catch (Exception expt)
  335. {
  336. MessageBox.Show(
  337. expt.InnerException == null ?
  338. expt.Message
  339. : expt.InnerException.Message);
  340. //special case, then there is no parameter and the method throw an exception
  341. //break the loop
  342. if (methodInfoRef.GetParameters().Length == 0)
  343. break;
  344. }
  345. }
  346. };
  347. res.Add(operationMenuItem);
  348. }
  349. #endregion
  350. return res.ToArray();
  351. }
  352. private void BuildOperationMenuItem(IImage image)
  353. {
  354. Type typeOfImage = image.GetType();
  355. operationsToolStripMenuItem.DropDownItems.Clear();
  356. //check if the menu for the specific image type has been built before
  357. if (!_typeToToolStripMenuItemsDictionary.ContainsKey(typeOfImage))
  358. {
  359. //if not built, build it and save to the cache.
  360. _typeToToolStripMenuItemsDictionary.Add(
  361. typeOfImage,
  362. BuildOperationTree(Reflection.ReflectIImage.GetImageMethods(image))
  363. );
  364. }
  365. operationsToolStripMenuItem.DropDownItems.AddRange(_typeToToolStripMenuItemsDictionary[typeOfImage]);
  366. }
  367. private void loadImageToolStripMenuItem_Click(object sender, EventArgs e)
  368. {
  369. if (loadImageFromFileDialog.ShowDialog() == DialogResult.OK)
  370. try
  371. {
  372. String filename = loadImageFromFileDialog.FileName;
  373. Image = new Image<Bgr, byte>(filename);
  374. }
  375. catch (Exception excpt)
  376. {
  377. MessageBox.Show(excpt.Message);
  378. }
  379. }
  380. private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
  381. {
  382. if (saveImageToFileDialog.ShowDialog() == DialogResult.OK)
  383. try
  384. {
  385. DisplayedImage.Save(saveImageToFileDialog.FileName);
  386. }
  387. catch (Exception excpt)
  388. {
  389. MessageBox.Show(excpt.Message);
  390. }
  391. }
  392. private void propertyToolStripMenuItem_Click(object sender, EventArgs e)
  393. {
  394. EnablePropertyPanel = !EnablePropertyPanel;
  395. }
  396. private ImageProperty ImagePropertyPanel
  397. {
  398. get
  399. {
  400. return EnablePropertyPanel ? _propertyDlg.ImagePropertyPanel : null;
  401. }
  402. }
  403. /// <summary>
  404. /// Used for tracking the mouse position on the image
  405. /// </summary>
  406. /// <param name="sender"></param>
  407. /// <param name="e"></param>
  408. private void onMouseMove(object sender, MouseEventArgs e)
  409. {
  410. if (EnablePropertyPanel)
  411. {
  412. IImage img = DisplayedImage;
  413. IColor color = img == null ?
  414. null :
  415. Reflection.ReflectIImage.GetPixelColor(img, e.Location);
  416. ImagePropertyPanel.MousePositionOnImage = e.Location;
  417. ImagePropertyPanel.ColorIntensity = color;
  418. }
  419. }
  420. /// <summary>
  421. /// The display mode for ImageBox
  422. /// </summary>
  423. public enum FunctionalModeOption
  424. {
  425. /// <summary>
  426. /// The ImageBox is only used for displaying image.
  427. /// No right-click menu available
  428. /// </summary>
  429. Minimum = 0,
  430. /// <summary>
  431. /// This is the ImageBox with all functions enabled.
  432. /// </summary>
  433. Everything = 1
  434. }
  435. /// <summary>
  436. /// Release the this Imagebox and all memory associate with it.
  437. /// </summary>
  438. public virtual new void Dispose()
  439. {
  440. if (this.Image != null)
  441. this.Image.Dispose();
  442. base.Dispose();
  443. }
  444. }
  445. }