/// <summary> /// Screen capture to file /// </summary> /// <param name="screen">The screen want to capture</param> /// <param name="filePath">Save image file path</param> /// <param name="imageFormat">Image format</param> /// <param name="bounds">bounds</param> /// <param name="timeOut">timeOut</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns></returns> public async Task ScreenCaptureToFile(Screen screen, string filePath, ImageFormat imageFormat = null, Rectangle?bounds = null, TimeSpan?timeOut = null, CancellationToken cancellationToken = default) { if (WindowsApi.Delay.HasValue) { await Task.Delay(WindowsApi.Delay.Value, cancellationToken); } var linkedToken = timeOut == null ? cancellationToken : CancellationTokenSource .CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(timeOut.Value).Token) .Token; using (var screenPixel = await InnerScreenCapture(GetValidIntersectRectangle(screen, bounds), linkedToken)) { if (screenPixel != null) { linkedToken.ThrowIfCancellationRequested(); if (imageFormat != null) { screenPixel.Save(filePath, imageFormat); WindowsApi.WriteLog( $"{nameof(ScreenCaptureToFile)} Save to {filePath} with {nameof(imageFormat)}:{imageFormat}"); } else { screenPixel.Save(filePath); WindowsApi.WriteLog($"{nameof(ScreenCaptureToFile)} Save to {filePath}"); } } } }
/// <summary> /// Get color at point. /// </summary> /// <param name="point">point</param> /// <returns>color</returns> public Color GetColorAt(Point point) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } var screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb); using (var dest = Graphics.FromImage(screenPixel)) { using (var src = Graphics.FromHwnd(IntPtr.Zero)) { var hSrcDc = src.GetHdc(); var hDc = dest.GetHdc(); BitBlt(hDc, 0, 0, 1, 1, hSrcDc, point.X, point.Y, (int)CopyPixelOperation.SourceCopy); dest.ReleaseHdc(); src.ReleaseHdc(); } } var color = screenPixel.GetPixel(0, 0); WindowsApi.WriteLog( $"{nameof(GetColorAt)} {nameof(point.X)}:{point.X},{nameof(point.Y)}:{point.Y} {nameof(color)}:{color}"); return(color); }
internal HotKeyHelper() { HotkeyManager.HotkeyAlreadyRegistered += (sender, args) => { WindowsApi.WriteLog($"Hot key {args.Name} already register"); HotkeyAlreadyRegistered?.Invoke(sender, args); }; }
/// <summary> /// Get color at point. /// </summary> /// <param name="point">point</param> /// <param name="wantColor">want color</param> /// <param name="timeOut">timeOut</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns>If wait unit time out, the return color is null.</returns> public async Task <bool> WaitColorNotAt(Point point, Color wantColor, TimeSpan?timeOut = null, CancellationToken cancellationToken = default) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } Color?color; var linkedToken = timeOut == null ? cancellationToken : CancellationTokenSource .CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(timeOut.Value).Token) .Token; var getColor = false; await Task.Run(async() => { using (var screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) { do { linkedToken.ThrowIfCancellationRequested(); using (var dest = Graphics.FromImage(screenPixel)) { using (var src = Graphics.FromHwnd(IntPtr.Zero)) { var hSrcDc = src.GetHdc(); var hDc = dest.GetHdc(); BitBlt(hDc, 0, 0, 1, 1, hSrcDc, point.X, point.Y, (int)CopyPixelOperation.SourceCopy); dest.ReleaseHdc(); src.ReleaseHdc(); } } color = screenPixel.GetPixel(0, 0); await Task.Delay(5, linkedToken); WindowsApi.WriteLog( $"{nameof(GetColorAt)} {nameof(point.X)}:{point.X},{nameof(point.Y)}:{point.Y} {nameof(color)}:{color.Value}"); if (color != wantColor) { getColor = true; WindowsApi.WriteLog( $"{nameof(WaitColorAt)} {nameof(point.X)}:{point.X},{nameof(point.Y)}:{point.Y} {nameof(color)} isn't {color.Value}"); } } while (!getColor); } }, linkedToken); return(getColor); }
/// <summary> /// 鼠标抬起 /// </summary> /// <param name="rightButton">右键</param> public void MouseButtonUp(bool rightButton = false) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } mouse_event(rightButton ? MouseEventFlag.RightUp : MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero); WindowsApi.WriteLog($"{nameof(MouseButtonUp)} {GetButtonString(rightButton)}"); }
/// <summary> /// KeyBoard down /// </summary> /// <param name="key">key name</param> public void KeyDown(Key key) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } keybd_event((byte)KeyInterop.VirtualKeyFromKey(key), 0, KeyDownFlag, IntPtr.Zero); WindowsApi.WriteLog($"{nameof(KeyDown)} {key}"); }
/* * /// <summary> * /// 鼠标按压 * /// </summary> * /// <param name="offsetX">offsetX</param> * /// <param name="offsetY">offsetY</param> * /// <param name="rightButton">右键</param> * /// <param name="pressedMillionSeconds">按压时长</param> * /// <param name="cancellationToken">cancellationToken</param> * [Obsolete("It's imprecise.", true)] * public async Task MousePressed(int offsetX, int offsetY, bool rightButton = false, uint pressedMillionSeconds = MousePressedTime, CancellationToken cancellationToken = default) * { * MouseMove(offsetX, offsetY); * await MousePressed(rightButton, pressedMillionSeconds, cancellationToken); * } */ /// <summary> /// 鼠标滚轮 /// </summary> /// <param name="wheelDelta">正值表明鼠标轮向前转动,即远离用户的方向;负值表明鼠标轮向后转动,即朝向用户。</param> public void MouseWheel(int wheelDelta = 500) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } mouse_event(MouseEventFlag.Wheel, 0, 0, wheelDelta, UIntPtr.Zero); WindowsApi.WriteLog($"{nameof(MouseWheel)} {nameof(wheelDelta)}:{wheelDelta}"); }
/// <summary> /// Activate the form using the form handle. /// </summary> /// <param name="hWnd">form handle</param> public void SwitchToThisWindow(IntPtr hWnd) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } //激活显示在最前面 SwitchToThisWindow(hWnd, true); WindowsApi.WriteLog($"{nameof(SwitchToThisWindow)} handle is {hWnd}."); }
/* * /// <summary> * /// 鼠标移动到相对当前鼠标的位置 * /// </summary> * /// <param name="offsetX">offsetX</param> * /// <param name="offsetY">offsetY</param> * [Obsolete("It's imprecise.", true)] * public void MouseMove(int offsetX, int offsetY) * { * if (WindowsApi.Delay.HasValue) * { * Thread.Sleep(WindowsApi.Delay.Value); * } * * mouse_event(MouseEventFlag.Move, offsetX, offsetY, 0, UIntPtr.Zero); * WindowsApi.WriteLog($"{nameof(MouseMove)} {nameof(offsetX)}:{offsetX},{nameof(offsetY)}:{offsetY}"); * } */ /// <summary> /// 鼠标移动到绝对位置 /// </summary> /// <param name="point">鼠标要移动到的绝对位置</param> /// <param name="delayPerPixel">每像素停留时间</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns></returns> public async Task MouseMove(MousePoint point, uint delayPerPixel, CancellationToken cancellationToken = default) { if (WindowsApi.Delay.HasValue) { await Task.Delay(WindowsApi.Delay.Value, cancellationToken); } await Task.Run(async() => { var currentPoint = GetCurrentMousePoint(); var currentX = currentPoint.X; var currentY = currentPoint.Y; var delayTimeSpan = TimeSpan.FromMilliseconds(delayPerPixel); while (currentPoint != point) { cancellationToken.ThrowIfCancellationRequested(); if (currentX < point.X) { currentX++; } else if (currentX > point.X) { currentX--; } if (currentY < point.Y) { currentY++; } else if (currentY > point.Y) { currentY--; } currentPoint = new MousePoint(currentX, currentY); var bounds = _innerScreenApi.Value.GetMouseScreen(currentPoint).Bounds; mouse_event(MouseEventFlag.Absolute | MouseEventFlag.Move, currentPoint.X * (MagicNumber / bounds.Width), currentPoint.Y * (MagicNumber / bounds.Height), 0, UIntPtr.Zero); if (delayPerPixel > 0u) { await Task.Delay(delayTimeSpan, cancellationToken); } } WindowsApi.WriteLog( $"{nameof(MouseMove)} from {nameof(MousePoint.X)}:{currentPoint.X},{nameof(MousePoint.Y)}:{currentPoint.Y} to {nameof(MousePoint.X)}:{point.X},{nameof(MousePoint.Y)}:{point.Y}"); }, cancellationToken); }
/// <summary> /// KeyBoard up /// </summary> /// <param name="keys">key names</param> public void KeyUp(params Key[] keys) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } foreach (var key in keys) { keybd_event((byte)KeyInterop.VirtualKeyFromKey(key), 0, KeyUpFlag, IntPtr.Zero); WindowsApi.WriteLog($"{nameof(KeyUp)} {key}"); } }
/// <summary> /// Precision match /// </summary> /// <param name="wantBitmap">Want match bitmap</param> /// <param name="bitmap">target bitmap</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns>Target bitmap location</returns> private async Task <Rectangle?> PrecisionMatchLocation(Bitmap wantBitmap, Bitmap bitmap, CancellationToken cancellationToken) { return(await Task.Run(() => { var index = 0; for (var x = 0; x <= bitmap.Size.Width - wantBitmap.Width; x++) { for (var y = 0; y <= bitmap.Size.Height - wantBitmap.Height; y++) { var isMatch = true; for (var x2 = 0; x2 < wantBitmap.Size.Width; x2++) { for (var y2 = 0; y2 < wantBitmap.Size.Height; y2++) { //降低检查频率 if (index++ % 10 == 0) { cancellationToken.ThrowIfCancellationRequested(); } if (bitmap.GetPixel(x + x2, y + y2) != wantBitmap.GetPixel(x2, y2)) { isMatch = false; break; } if (x2 == wantBitmap.Size.Width - 1 && y2 == wantBitmap.Size.Height - 1) { var rectangle = new Rectangle?(new Rectangle(x, y, wantBitmap.Width, wantBitmap.Height)); WindowsApi.WriteLog($"{nameof(PrecisionMatchLocation)} match success, {rectangle}"); return rectangle; } } if (!isMatch) { break; } } } } WindowsApi.WriteLog($"{nameof(PrecisionMatchLocation)} match failed"); return null; }, cancellationToken)); }
/// <summary> /// 鼠标移动到绝对位置 /// </summary> /// <param name="point">需要移动到的坐标</param> public void MouseMove(MousePoint point) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } var bounds = _innerScreenApi.Value.GetMouseScreen(point).Bounds; mouse_event(MouseEventFlag.Absolute | MouseEventFlag.Move, point.X * (MagicNumber / bounds.Width), point.Y * (MagicNumber / bounds.Height), 0, UIntPtr.Zero); WindowsApi.WriteLog($"{nameof(MouseMove)} {nameof(MousePoint.X)}:{point.X},{nameof(MousePoint.Y)}:{point.Y}"); }
/* * /// <summary> * /// 鼠标双击 * /// </summary> * /// <param name="offsetX">offsetX</param> * /// <param name="offsetY">offsetY</param> * /// <param name="rightButton">右键</param> * [Obsolete("It's imprecise.", true)] * public void MouseDoubleClick(int offsetX, int offsetY, bool rightButton = false) * { * MouseMove(offsetX, offsetY); * MouseDoubleClick(rightButton); * } */ /// <summary> /// 鼠标按压 /// </summary> /// <param name="rightButton">右键</param> /// <param name="pressedMillionSeconds">按压时长</param> /// <param name="cancellationToken">cancellationToken</param> public async Task MousePressed(bool rightButton = false, uint pressedMillionSeconds = MousePressedTime, CancellationToken cancellationToken = default) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } await Task.Run(async() => { mouse_event(rightButton ? MouseEventFlag.RightDown : MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero); await Task.Delay(TimeSpan.FromMilliseconds(pressedMillionSeconds), cancellationToken); mouse_event(rightButton ? MouseEventFlag.RightUp : MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero); WindowsApi.WriteLog( $"{nameof(MousePressed)} {GetButtonString(rightButton)} {nameof(MousePressedTime)}:{pressedMillionSeconds}"); }, cancellationToken); }
/// <summary> /// Template match /// </summary> /// <param name="wantBitmap">Want match bitmap</param> /// <param name="bitmap">target bitmap</param> /// <param name="templateMatch">template match option</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns>Target bitmap location</returns> private async Task <Rectangle?> TemplateMatchLocation(Bitmap wantBitmap, Bitmap bitmap, TemplateMatch templateMatch, CancellationToken cancellationToken) { return(await Task.Run(() => { try { cancellationToken.ThrowIfCancellationRequested(); using var srcMat = bitmap.ToMat(); using var dstMat = wantBitmap.ToMat(); using var outArray = OutputArray.Create(srcMat); cancellationToken.ThrowIfCancellationRequested(); Cv2.MatchTemplate(srcMat, dstMat, outArray, templateMatch.TemplateMatchModel); cancellationToken.ThrowIfCancellationRequested(); Cv2.MinMaxLoc(InputArray.Create(outArray.GetMat() !), out _, out var maxValue, out _, out var point); if (maxValue >= templateMatch.Threshold && maxValue <= 1d) { var rectangle = new Rectangle?(new Rectangle(point.X, point.Y, wantBitmap.Width, wantBitmap.Height)); WindowsApi.WriteLog( $"{nameof(TemplateMatchLocation)} match success, {nameof(TemplateMatch.TemplateMatchModel)}:{templateMatch.TemplateMatchModel}, {nameof(TemplateMatch.Threshold)}:{templateMatch.Threshold}, {nameof(maxValue)}:{maxValue}, {rectangle}"); return rectangle; } else { WindowsApi.WriteLog( $"{nameof(TemplateMatchLocation)} match failed, {nameof(TemplateMatch.TemplateMatchModel)}:{templateMatch.TemplateMatchModel}, {nameof(TemplateMatch.Threshold)}:{templateMatch.Threshold}, {nameof(maxValue)}:{maxValue}"); } } catch (Exception ex) { WindowsApi.WriteLog( $"{nameof(TemplateMatchLocation)} {nameof(TemplateMatch.TemplateMatchModel)}:{templateMatch.TemplateMatchModel}, {nameof(TemplateMatch.Threshold)}:{templateMatch.Threshold}, ErrorMessage:{ex.Message}"); } return null; }, cancellationToken)); }
/// <summary> /// Input text /// </summary> /// <param name="text">text</param> public void InputString(string text) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } if (text != null) { SendKeys.SendWait(text); WindowsApi.WriteLog($"{nameof(InputString)} {nameof(text)} is \"{text}\"."); } else { WindowsApi.WriteLog($"{nameof(InputString)} {nameof(text)} is null."); } }
/// <summary> /// 获取当前的鼠标坐标 /// </summary> /// <returns></returns> public MousePoint GetCurrentMousePoint() { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } if (GetCursorPos(out var p)) { WindowsApi.WriteLog($"{nameof(GetCurrentMousePoint)} {nameof(MousePoint.X)}:{p.X},{nameof(MousePoint.Y)}:{p.Y}"); return(p); } else { WindowsApi.WriteLog($"{nameof(GetCurrentMousePoint)} operating failed."); return(new MousePoint()); } }
/* * * /// <summary> * /// Activate the process if it not minimize. * /// </summary> * /// <param name="process">process</param> * /// <returns>success</returns> * public bool SetForegroundWindow(Process process) * { * var result = false; * * if (WindowsApi.Delay.HasValue) * { * Thread.Sleep(WindowsApi.Delay.Value); * } * * if (process != null) * { * result = SetForegroundWindow(process.MainWindowHandle); * WindowsApi.WriteLog( * $"{nameof(SetForegroundWindow)} {nameof(process)} name is {process.ProcessName}, main window handle is {process.MainWindowHandle}, {nameof(result)} is {result}."); * } * else * { * WindowsApi.WriteLog($"{nameof(SetForegroundWindow)} {nameof(process)} is null."); * } * * return result; * } * * /// <summary> * /// Activate the form using the form handle if it not minimize. * /// </summary> * /// <param name="hWnd">form handle</param> * /// <returns>success</returns> * public bool SetForegroundWindowWithHandle(IntPtr hWnd) * { * if (WindowsApi.Delay.HasValue) * { * Thread.Sleep(WindowsApi.Delay.Value); * } * * var result = SetForegroundWindow(hWnd); * WindowsApi.WriteLog($"{nameof(SetForegroundWindow)} handle is {hWnd}, {nameof(result)} is {result}."); * * return result; * } * */ #endregion /// <summary> /// Activate the process. /// </summary> /// <param name="process"></param> public void SwitchToThisWindow(Process process) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } if (process != null) { SwitchToThisWindow(process.MainWindowHandle, true); WindowsApi.WriteLog( $"{nameof(SwitchToThisWindow)} {nameof(process)} name is {process.ProcessName}, main window handle is {process.MainWindowHandle}."); } else { WindowsApi.WriteLog($"{nameof(SwitchToThisWindow)} {nameof(process)} is null."); } }
/// <summary> /// KeyPress /// </summary> /// <param name="key">key name</param> /// <param name="pressedMillionSeconds">pressed time</param> /// <returns></returns> public static async Task KeyPress(Key key, uint pressedMillionSeconds = KeyPressedTime) { if (WindowsApi.Delay.HasValue) { Thread.Sleep(WindowsApi.Delay.Value); } var keyByte = (byte)KeyInterop.VirtualKeyFromKey(key); keybd_event(keyByte, 0, KeyDownFlag, IntPtr.Zero); if (pressedMillionSeconds != 0u) { await Task.Delay(TimeSpan.FromMilliseconds(pressedMillionSeconds)); } keybd_event(keyByte, 0, KeyUpFlag, IntPtr.Zero); WindowsApi.WriteLog($"{nameof(KeyPress)} {key} {nameof(KeyPressedTime)}:{pressedMillionSeconds}"); }
/// <summary> /// Screen capture to stream /// </summary> /// <param name="screen">The screen want to capture</param> /// <param name="imageFormat">Save image file path</param> /// <param name="bounds">bounds</param> /// <param name="timeOut">timeOut</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns></returns> public async Task <Stream> ScreenCaptureToStream(Screen screen, ImageFormat imageFormat = null, Rectangle?bounds = null, TimeSpan?timeOut = null, CancellationToken cancellationToken = default) { if (WindowsApi.Delay.HasValue) { await Task.Delay(WindowsApi.Delay.Value, cancellationToken); } var linkedToken = timeOut == null ? cancellationToken : CancellationTokenSource .CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(timeOut.Value).Token) .Token; using (var screenPixel = await InnerScreenCapture(GetValidIntersectRectangle(screen, bounds), linkedToken)) { if (screenPixel != null) { linkedToken.ThrowIfCancellationRequested(); var stream = new MemoryStream(); if (imageFormat != null) { screenPixel.Save(stream, imageFormat); WindowsApi.WriteLog( $"{nameof(ScreenCaptureToStream)} save to stream with {nameof(imageFormat)}:{imageFormat}"); } else { screenPixel.Save(stream, ImageFormat.Bmp); WindowsApi.WriteLog( $"{nameof(ScreenCaptureToStream)} save to stream with {nameof(imageFormat)}:{ImageFormat.MemoryBmp}"); } return(stream); } } return(null); }
/// <summary> /// 注册或替换快捷键 /// </summary> /// <param name="identity"></param> /// <param name="key"></param> /// <param name="modifierKeys"></param> /// <param name="action"></param> /// <returns></returns> public bool RegisterOrReplace(string identity, Key key, ModifierKeys modifierKeys, Action action) { lock (_lock) { try { Remove(identity); var hotKeyIdentity = new HotKeyIdentity(key, modifierKeys); if (_hotKeyCache.ContainsKey(hotKeyIdentity)) { _hotKeyCache[hotKeyIdentity].LinkedEventHandler(action, identity); } else { var hotKeyModel = new HotKeyModel(hotKeyIdentity); hotKeyModel.RemoveAllLinkedEvent += HotKeyModelOnRemoveAllLinkedEvent; hotKeyModel.LinkedEventHandler(action, identity); _hotKeyCache.Add(hotKeyIdentity, hotKeyModel); } if (!string.IsNullOrEmpty(identity)) { _identityCache.Add(identity, hotKeyIdentity); } return(true); } catch (HotkeyAlreadyRegisteredException hotkeyAlreadyRegisteredException) { WindowsApi.WriteLog($"Hot key {hotkeyAlreadyRegisteredException.Name} already register"); HotkeyAlreadyRegistered?.Invoke(HotkeyManager.Current, new HotkeyAlreadyRegisteredEventArgs(hotkeyAlreadyRegisteredException.Name)); } } return(false); }
/// <summary> /// 处理事件 /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnHotkeyEventHandler(object sender, HotkeyEventArgs args) { if (_linkedEventHandlers != null) { lock (_linkedEventHandlers) { if (_linkedEventHandlers.RemoveAll(s => !s.IsAlive()) > 0 && _linkedEventHandlersWithIdentity != null) { var removeIdentity = new List <string>(); foreach (var keyValuePair in _linkedEventHandlersWithIdentity) { if (!keyValuePair.Value.IsAlive()) { removeIdentity.Add(keyValuePair.Key); } } foreach (var identity in removeIdentity) { _linkedEventHandlersWithIdentity.Remove(identity); } } foreach (var linkedEventHandler in _linkedEventHandlers) { try { linkedEventHandler.Trigger(); } catch (Exception e) { WindowsApi.WriteLog(e.Message); Debug.WriteLine(e); } } } } }
/// <summary> /// Inner screen capture /// </summary> /// <param name="realRectangle">The real rectangle</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns>not null</returns> private async Task <Bitmap> InnerScreenCapture(Rectangle realRectangle, CancellationToken cancellationToken = default) { if (realRectangle.Width > 0 && realRectangle.Height > 0) { return(await Task.Run(() => { var screenPixel = new Bitmap(realRectangle.Width, realRectangle.Height, PixelFormat.Format32bppArgb); cancellationToken.ThrowIfCancellationRequested(); using (var dest = Graphics.FromImage(screenPixel)) { using (var src = Graphics.FromHwnd(IntPtr.Zero)) { var hSrcDc = src.GetHdc(); var hDc = dest.GetHdc(); BitBlt(hDc, 0, 0, realRectangle.Width, realRectangle.Height, hSrcDc, realRectangle.X, realRectangle.Y, (int)CopyPixelOperation.SourceCopy); dest.ReleaseHdc(); src.ReleaseHdc(); } } WindowsApi.WriteLog($"{nameof(InnerScreenCapture)} {nameof(realRectangle)}:{realRectangle}"); return screenPixel; }, cancellationToken)); } else { WindowsApi.WriteLog($"{nameof(InnerScreenCapture)} failed. {nameof(realRectangle)}:{realRectangle}"); throw new InvalidOperationException( $"Can't capture from {nameof(realRectangle)} is {realRectangle}"); } }
/// <summary> /// Surf match /// </summary> /// <param name="wantBitmap">Want match bitmap</param> /// <param name="bitmap">target bitmap</param> /// <param name="surfMatch">surf match option</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns>Target bitmap location</returns> private async Task <Rectangle?> SurfMatchLocation(Bitmap wantBitmap, Bitmap bitmap, SurfMatch surfMatch, CancellationToken cancellationToken) { return(await Task.Run(() => { try { using (var matSrc = bitmap.ToMat()) using (var matTo = wantBitmap.ToMat()) using (var matSrcRet = new Mat()) using (var matToRet = new Mat()) { cancellationToken.ThrowIfCancellationRequested(); KeyPoint[] keyPointsSrc, keyPointsTo; using (var surf = SURF.Create(surfMatch.HessianThreshold, 4, 3, true, true)) { surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet); surf.DetectAndCompute(matTo, null, out keyPointsTo, matToRet); } cancellationToken.ThrowIfCancellationRequested(); using (var flnMatcher = new FlannBasedMatcher()) { var matches = flnMatcher.Match(matSrcRet, matToRet); cancellationToken.ThrowIfCancellationRequested(); //求最小最大距离 var minDistance = 1000d; //反向逼近 var maxDistance = 0d; for (int i = 0; i < matSrcRet.Rows; i++) { var distance = matches[i].Distance; if (distance > maxDistance) { maxDistance = distance; } if (distance < minDistance) { minDistance = distance; } } var pointsSrc = new List <Point2f>(); var pointsDst = new List <Point2f>(); for (int i = 0; i < matSrcRet.Rows; i++) { double distance = matches[i].Distance; if (distance < Math.Max(minDistance * 2, 0.02)) { pointsSrc.Add(keyPointsSrc[matches[i].QueryIdx].Pt); pointsDst.Add(keyPointsTo[matches[i].TrainIdx].Pt); } } if (pointsSrc.Count > 0 && pointsDst.Count > 0) { var location = pointsSrc[0] - pointsDst[0]; var rectangle = new Rectangle?(new Rectangle((int)location.X, (int)location.Y, wantBitmap.Width, wantBitmap.Height)); WindowsApi.WriteLog( $"{nameof(SurfMatchLocation)} match success, {nameof(maxDistance)}:{maxDistance};{nameof(minDistance)}:{minDistance} match count:{pointsSrc.Count}, {rectangle}"); return rectangle; } else { WindowsApi.WriteLog( $"{nameof(SurfMatchLocation)} match failed, {nameof(maxDistance)}:{maxDistance};{nameof(minDistance)}:{minDistance}"); } } } } catch (Exception ex) { WindowsApi.WriteLog($"{nameof(SurfMatchLocation)} ErrorMessage:{ex.Message}"); } return null; }, cancellationToken)); }
/// <summary> /// Sift match /// </summary> /// <param name="wantBitmap">Want match bitmap</param> /// <param name="bitmap">target bitmap</param> /// <param name="cancellationToken">cancellationToken</param> /// <returns>Target bitmap location</returns> private async Task <Rectangle?> SiftMatchLocation(Bitmap wantBitmap, Bitmap bitmap, CancellationToken cancellationToken) { return(await Task.Run(() => { try { using var matSrc = bitmap.ToMat(); using var matTo = wantBitmap.ToMat(); using var matSrcRet = new Mat(); using var matToRet = new Mat(); cancellationToken.ThrowIfCancellationRequested(); KeyPoint[] keyPointsSrc, keyPointsTo; using (var sift = SIFT.Create()) { sift.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet); sift.DetectAndCompute(matTo, null, out keyPointsTo, matToRet); } cancellationToken.ThrowIfCancellationRequested(); using (var bfMatcher = new BFMatcher()) { var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 2); cancellationToken.ThrowIfCancellationRequested(); var pointsSrc = new List <Point2f>(); var pointsDst = new List <Point2f>(); foreach (var items in matches.Where(x => x.Length > 1)) { if (items[0].Distance < 0.5 * items[1].Distance) { pointsSrc.Add(keyPointsSrc[items[0].QueryIdx].Pt); pointsDst.Add(keyPointsTo[items[0].TrainIdx].Pt); } } if (pointsSrc.Count > 0 && pointsDst.Count > 0) { var location = pointsSrc[0] - pointsDst[0]; var rectangle = new Rectangle?(new Rectangle((int)location.X, (int)location.Y, wantBitmap.Width, wantBitmap.Height)); WindowsApi.WriteLog( $"{nameof(SiftMatchLocation)} match success, match count:{pointsSrc.Count}, {rectangle}"); return rectangle; } else { WindowsApi.WriteLog( $"{nameof(SiftMatchLocation)} match failed"); } } } catch (Exception ex) { WindowsApi.WriteLog($"{nameof(SiftMatchLocation)} ErrorMessage:{ex.Message}"); } return null; }, cancellationToken)); }