MainWindow.xaml.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Input;
  6. using System.Windows.Media.Imaging;
  7. using Microsoft.Win32;
  8. using System.Drawing;
  9. using System.IO;
  10. using System.Xml.Serialization;
  11. using System.Threading.Tasks;
  12. using System.Threading;
  13. namespace ConstRectCropImage
  14. {
  15. enum EnumResultImgType
  16. {
  17. OrigImgWithCropRect,
  18. CroppedImg,
  19. }
  20. /// <summary>
  21. /// MainWindow.xaml 的交互逻辑
  22. /// </summary>
  23. public partial class MainWindow : Window
  24. {
  25. #region private variables
  26. private BitmapImage _origimg;
  27. private volatile int _currentIndex = 0;
  28. private string _currentImgFolder;
  29. private List<ImgInfoForSegEntity> _cropdatas = new List<ImgInfoForSegEntity>();
  30. private System.Windows.Media.Brush _roiColor = System.Windows.Media.Brushes.Orange;
  31. private System.Drawing.Point _startpoint;
  32. private System.Drawing.Point _endpoint;
  33. private System.Drawing.Rectangle _drawingroi = System.Drawing.Rectangle.Empty;
  34. private readonly ManualResetEvent _drawFinishEvent = new ManualResetEvent(false);
  35. #endregion
  36. public MainWindow()
  37. {
  38. InitializeComponent();
  39. _origimg = null;
  40. OrigImage.Source = _origimg;
  41. }
  42. private void OnLoadMultiImagesClick(object sender, RoutedEventArgs e)
  43. {
  44. if (_cropdatas.Count > 0)
  45. {
  46. MessageBox.Show("请先完成已加载的图像.");
  47. return;
  48. }
  49. System.Windows.Forms.FolderBrowserDialog folderDialog = new System.Windows.Forms.FolderBrowserDialog();
  50. folderDialog.Description = "请选择需要裁切的文件夹";
  51. if (folderDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  52. {
  53. if (string.IsNullOrEmpty(folderDialog.SelectedPath))
  54. {
  55. MessageBox.Show("保存裁切后图像的文件夹不能为空!");
  56. return;
  57. }
  58. _currentImgFolder = folderDialog.SelectedPath;
  59. DirectoryInfo selectedFolder = new DirectoryInfo(folderDialog.SelectedPath);
  60. Queue<DirectoryInfo> foldersToSearch = new Queue<DirectoryInfo>();
  61. foldersToSearch.Enqueue(selectedFolder);
  62. while (foldersToSearch.Count > 0)
  63. {
  64. DirectoryInfo folder = foldersToSearch.Dequeue();
  65. if (!folder.Exists)
  66. {
  67. continue;
  68. }
  69. // 搜索子文件夹
  70. DirectoryInfo[] subFolders = folder.GetDirectories();
  71. foreach (DirectoryInfo subFolder in subFolders)
  72. {
  73. foldersToSearch.Enqueue(new DirectoryInfo(subFolder.FullName));
  74. }
  75. // 遍历当前文件夹中所有图片
  76. FileInfo[] files = folder.GetFiles();
  77. foreach (FileInfo file in files)
  78. {
  79. string fileName = file.FullName;
  80. string fileLocalPath = fileName.Substring(_currentImgFolder.Length + 1);
  81. string imgId = Path.GetFileNameWithoutExtension(fileName);
  82. ImgInfoForSegEntity imgInfo = new ImgInfoForSegEntity
  83. {
  84. ImgId = imgId,
  85. ImgLocalPath = fileLocalPath,
  86. };
  87. _cropdatas.Add(imgInfo);
  88. }
  89. }
  90. _currentIndex = 0;
  91. CurrentIndexUpdated();
  92. }
  93. }
  94. private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
  95. {
  96. MyCanvas.Width = GridOrigImg.ActualWidth;
  97. MyCanvas.Height = GridOrigImg.ActualHeight;
  98. ImageUpdated();
  99. DrawingROIUpdated();
  100. }
  101. private void Canvas_MouseLeftBtnDown(object sender, MouseButtonEventArgs e)
  102. {
  103. int x = Convert.ToInt32(e.GetPosition(OrigImage).X / OrigImage.ActualWidth * _origimg.Width);
  104. int y = Convert.ToInt32(e.GetPosition(OrigImage).Y / OrigImage.ActualHeight * _origimg.Height);
  105. _startpoint = new System.Drawing.Point(x, y);
  106. }
  107. private void Canvas_MouseMove(object sender, MouseEventArgs e)
  108. {
  109. int x = Convert.ToInt32(e.GetPosition(OrigImage).X / OrigImage.ActualWidth * _origimg.Width);
  110. int y = Convert.ToInt32(e.GetPosition(OrigImage).Y / OrigImage.ActualHeight * _origimg.Height);
  111. MousePosition.Text = x.ToString() + "," + y.ToString();
  112. if (e.LeftButton == MouseButtonState.Pressed)
  113. {
  114. _endpoint = new System.Drawing.Point(x, y);
  115. DrawingROIUpdated();
  116. }
  117. }
  118. private void SaveCropDatasClick(object sender, RoutedEventArgs e)
  119. {
  120. if (_cropdatas.Count <= 0)
  121. {
  122. MessageBox.Show("未找到需保存的数据");
  123. return;
  124. }
  125. if (!_cropdatas[_currentIndex].SegSucceed)
  126. {
  127. if (!UseCurrentRoi())
  128. {
  129. return;
  130. }
  131. }
  132. EnumResultImgType resulttype = EnumResultImgType.OrigImgWithCropRect;
  133. if (RadioBtnResultCropped.IsChecked ?? false)
  134. {
  135. resulttype = EnumResultImgType.CroppedImg;
  136. }
  137. System.Windows.Forms.FolderBrowserDialog dstFolderD = new System.Windows.Forms.FolderBrowserDialog();
  138. dstFolderD.Description = "请选择保存裁切后图像的文件夹";
  139. if (dstFolderD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  140. {
  141. if (string.IsNullOrEmpty(dstFolderD.SelectedPath))
  142. {
  143. MessageBox.Show("保存裁切后图像的文件夹不能为空!");
  144. return;
  145. }
  146. Task.Run(() =>
  147. {
  148. try
  149. {
  150. foreach (ImgInfoForSegEntity img in _cropdatas)
  151. {
  152. string filename = img.ImgLocalPath;
  153. Bitmap img_orig = new Bitmap(Bitmap.FromFile(_currentImgFolder + "\\" + filename));
  154. int imgWidth = img_orig.Width;
  155. int imgHeight = img_orig.Height;
  156. if (!img.SegSucceed || (img.Left == 0 && img.Right == 0 && img.Top == 0 && img.Bottom == 0))
  157. {
  158. if (imgWidth <= _drawingroi.Right || imgHeight <= _drawingroi.Height)
  159. {
  160. _drawFinishEvent.Reset();
  161. MessageBox.Show("请重新画框!");
  162. _drawFinishEvent.WaitOne();
  163. }
  164. img.Left = _drawingroi.Left;
  165. img.Top = _drawingroi.Top;
  166. img.Right = _drawingroi.Right;
  167. img.Bottom = _drawingroi.Bottom;
  168. img.SegSucceed = true;
  169. }
  170. Bitmap dstimg = null;
  171. if (resulttype == EnumResultImgType.CroppedImg)
  172. {
  173. dstimg = new Bitmap(_drawingroi.Width, _drawingroi.Height, img_orig.PixelFormat);
  174. using (var g = Graphics.FromImage(dstimg))
  175. {
  176. g.DrawImage(img_orig, new System.Drawing.Rectangle(0, 0, _drawingroi.Width, _drawingroi.Height),
  177. new System.Drawing.Rectangle(_drawingroi.Left, _drawingroi.Top, _drawingroi.Width, _drawingroi.Height),
  178. GraphicsUnit.Pixel);
  179. g.Dispose();
  180. }
  181. }
  182. if (resulttype == EnumResultImgType.OrigImgWithCropRect)
  183. {
  184. dstimg = img_orig.Clone(new Rectangle(0, 0, img_orig.Width, img_orig.Height), img_orig.PixelFormat);
  185. using (var g = Graphics.FromImage(dstimg))
  186. {
  187. g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Color.Yellow, 8), _drawingroi);
  188. g.Dispose();
  189. }
  190. }
  191. string fileRawName = Path.GetFileNameWithoutExtension(filename);
  192. dstimg.Save(dstFolderD.SelectedPath + "\\" + fileRawName+".jpg",System.Drawing.Imaging.ImageFormat.Jpeg);
  193. dstimg.Dispose();
  194. img_orig.Dispose();
  195. _currentIndex += 1;
  196. }
  197. XmlSerializer xmls = new XmlSerializer(_cropdatas.GetType());
  198. FileInfo fileinfo = new FileInfo(dstFolderD.SelectedPath + "\\croppeddatas.xml");
  199. if (fileinfo.Exists)
  200. {
  201. fileinfo.Delete();
  202. }
  203. using (Stream s = fileinfo.OpenWrite())
  204. {
  205. xmls.Serialize(s, _cropdatas);
  206. }
  207. _cropdatas.Clear();
  208. MessageBox.Show("完成");
  209. }
  210. catch (Exception excep)
  211. {
  212. MessageBox.Show("出错了!" + excep);
  213. }
  214. });
  215. }
  216. }
  217. private bool UseCurrentRoi()
  218. {
  219. if (!_startpoint.IsEmpty && !_endpoint.IsEmpty)
  220. {
  221. int roileft = Math.Min(_startpoint.X, _endpoint.X);
  222. int roiright = Math.Max(_startpoint.X, _endpoint.X);
  223. int roitop = Math.Min(_startpoint.Y, _endpoint.Y);
  224. int roibottom = Math.Max(_startpoint.Y, _endpoint.Y);
  225. if (roileft == roiright || roitop == roibottom)
  226. {
  227. MessageBox.Show("绘制的矩形框尺寸不能为0.");
  228. return false;
  229. }
  230. _drawingroi = new System.Drawing.Rectangle(roileft, roitop, roiright - roileft, roibottom - roitop);
  231. _cropdatas[_currentIndex].Left = roileft;
  232. _cropdatas[_currentIndex].Top = roitop;
  233. _cropdatas[_currentIndex].Right = roiright;
  234. _cropdatas[_currentIndex].Bottom = roibottom;
  235. _cropdatas[_currentIndex].SegSucceed = true;
  236. return true;
  237. }
  238. else
  239. {
  240. MessageBox.Show("请先加载待测图像并绘制有效的ROI");
  241. return false;
  242. }
  243. }
  244. private void DrawingROIUpdated()
  245. {
  246. if (null == _origimg)
  247. {
  248. return;
  249. }
  250. System.Windows.Shapes.Rectangle drawingroi = MyCanvas.FindName("drawingroi") as System.Windows.Shapes.Rectangle;
  251. if (drawingroi != null)
  252. {
  253. MyCanvas.Children.Remove(drawingroi);
  254. MyCanvas.UnregisterName("drawingroi");
  255. }
  256. if (_startpoint.IsEmpty || _endpoint.IsEmpty)
  257. {
  258. return;
  259. }
  260. // 在原始图像上的坐标
  261. int roileft = Math.Min(_startpoint.X, _endpoint.X);
  262. int roiright = Math.Max(_startpoint.X, _endpoint.X);
  263. int roitop = Math.Min(_startpoint.Y, _endpoint.Y);
  264. int roibottom = Math.Max(_startpoint.Y, _endpoint.Y);
  265. ROI.Text = roileft.ToString() + "," + roitop + "," + roiright.ToString() + "," + roibottom.ToString();
  266. // 求在画布上的坐标
  267. double boxTopInCanvas = roitop * OrigImgScaleTramsform.ScaleY;
  268. double boxLeftInCanvas = roileft * OrigImgScaleTramsform.ScaleX;
  269. double boxWidthInCanvas = (roiright - roileft) * OrigImgScaleTramsform.ScaleX;
  270. double boxHeghtInCanvas = (roibottom - roitop) * OrigImgScaleTramsform.ScaleY;
  271. // 在画布上画矩形
  272. drawingroi = new System.Windows.Shapes.Rectangle();
  273. drawingroi.StrokeThickness = 3;
  274. drawingroi.Stroke = _roiColor;
  275. drawingroi.Width = boxWidthInCanvas;
  276. drawingroi.Height = boxHeghtInCanvas;
  277. Canvas.SetLeft(drawingroi, boxLeftInCanvas);
  278. Canvas.SetTop(drawingroi, boxTopInCanvas);
  279. MyCanvas.Children.Add(drawingroi);
  280. MyCanvas.RegisterName("drawingroi", drawingroi);
  281. }
  282. private void ImageUpdated()
  283. {
  284. if (null == _origimg)
  285. {
  286. return;
  287. }
  288. // 缩放
  289. double ratioW = GridOrigImg.ActualWidth / _origimg.Width;
  290. double ratioH = GridOrigImg.ActualHeight / _origimg.Height;
  291. double ratio = ratioW < ratioH ? ratioW : ratioH;
  292. OrigImgScaleTramsform.CenterX = 0;
  293. OrigImgScaleTramsform.CenterY = 0;
  294. OrigImgScaleTramsform.ScaleX = ratio;
  295. OrigImgScaleTramsform.ScaleY = ratio;
  296. // 使图像居中,需要平移
  297. double translateX = 0;
  298. double translateY = 0;
  299. if (Math.Abs(ratio - ratioW) < 0.0001)
  300. {
  301. translateY = 0.5 * (ratioH - ratio) * _origimg.Height;
  302. }
  303. else
  304. {
  305. translateX = 0.5 * (ratioW - ratio) * _origimg.Width;
  306. }
  307. // 更新画布尺寸,使其刚好只覆盖图像区域
  308. MyCanvas.Width = ratio * _origimg.Width;
  309. MyCanvas.Height = ratio * _origimg.Height;
  310. Canvas.SetLeft(MyCanvas, translateX);
  311. Canvas.SetTop(MyCanvas, translateY);
  312. MyCanvas.ClipToBounds = true;
  313. OrigImage.Source = _origimg;
  314. }
  315. private void CurrentIndexUpdated()
  316. {
  317. ImgInfoForSegEntity imginfo = _cropdatas[_currentIndex];
  318. Bitmap image = new Bitmap(_currentImgFolder + "\\" + imginfo.ImgLocalPath);
  319. _origimg = ImageUtility.BitmapToBitmapImage(image);
  320. if (imginfo.Left != 0 || imginfo.Right != 0 || imginfo.Top != 0 || imginfo.Bottom != 0)
  321. {
  322. _startpoint = new System.Drawing.Point(imginfo.Left, imginfo.Top);
  323. _endpoint = new System.Drawing.Point(imginfo.Right, imginfo.Bottom);
  324. _drawingroi = new System.Drawing.Rectangle(imginfo.Left, imginfo.Top, imginfo.Right - imginfo.Left, imginfo.Bottom - imginfo.Top);
  325. }
  326. ImageUpdated();
  327. DrawingROIUpdated();
  328. ROI.Text = string.Empty;
  329. }
  330. private void UseCurrentRoiClick(object sender, RoutedEventArgs e)
  331. {
  332. if (UseCurrentRoi())
  333. {
  334. _drawFinishEvent.Set();
  335. }
  336. }
  337. }
  338. }