WPF实现截图功能——WPF制作文字OCR软件(三)

WPF实现截图功能

一、前言

上一篇文章已经介绍过了如何自定义窗口,对于一款文字OCR软件,如果只能打开本地图片进行识别,那就显得不够人性化,所以这篇文章主要介绍了WPF如何实现截图功能。


二、截图演示

截图演示

三、 代码实现

1. 截图原理

在制作这个功能前,我在网上查了一些资料,了解到截图的原理就是将当前屏幕的图像拷贝下来,再置顶,之后选择截图区域即可。

2. 拷贝当前屏幕图像

#region 获取屏幕图像
        /// <summary>
        /// 获取屏幕图像
        /// </summary>
        /// <returns>图像文件</returns>
        private Bitmap GetScreen()
        {
            System.Drawing.Rectangle rc = SystemInformation.VirtualScreen;	// 获取屏幕的宽和高
            Bitmap bitmap = new Bitmap(rc.Width, rc.Height);
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);		// 拷贝屏幕
            }
            return bitmap;
        }

#endregion
  • 使用SystemInformation.VirtualScreen时需要添加 using System.Windows.Forms;
  • BitmapGraphics均包含在System.Drawing

3. 截取图像

如果需要将屏幕图像贴在桌面最上面一层,可以通过将屏幕图像放在一个新窗口中呈现出来。
创建一个名为SnipWin的新窗口。

窗口样式代码如下:

<Window x:Class="SnipOCR.SnapWin"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SnipOCR"
        mc:Ignorable="d"
        Title="SnapWin" Height="450" Width="800" WindowStyle="None"  AllowsTransparency="True" Background="Transparent">
    <Grid>
        <InkCanvas Margin="0,0,0,0" Name="Ink" Visibility="Visible" EditingModeInverted="Select" EditingMode="Select" Cursor="Cross" MouseDown="Ink_MouseDown" MouseMove="Ink_MouseMove" MouseUp="Ink_MouseUp" MouseRightButtonDown="Ink_MouseRightButtonDown">
		</InkCanvas>
        <Border Name="Bord" Visibility="Collapsed" BorderThickness="2" HorizontalAlignment="Left"   VerticalAlignment="Top" Cursor="Cross" BorderBrush="#FF09646D"/>
        <Button Name="Conf" Visibility="Collapsed" Content="" FontSize="16" HorizontalAlignment="Left" Height="22" Margin="527,347,0,0" VerticalAlignment="Top" Width="30" BorderBrush="#FF32510C" Background="#66C4EF3C" Foreground="#FF006855" Click="Conf_Click"/>
    </Grid>
</Window>

下面是cs文件代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Drawing;
using System.Runtime.InteropServices;
using Rectangle = System.Drawing.Rectangle;
using System.Windows.Interop;

namespace SnipOCR
{
    /// <summary>
    /// SnapWin.xaml 的交互逻辑
    /// </summary>
    /// 
    public partial class SnapWin : Window
    {
        public delegate void SendMessage(Bitmap bitmap);
        public SendMessage sendMessage;
        #region Win32 API
        [DllImport("user32.dll")]
        static extern IntPtr GetDC(IntPtr ptr);
        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(
        IntPtr hdc, // handle to DC
        int nIndex // index of capability
        );
        [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
        static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
        #endregion
        #region DeviceCaps常量
        const int HORZRES = 8;
        const int VERTRES = 10;
        const int LOGPIXELSX = 88;
        const int LOGPIXELSY = 90;
        const int DESKTOPVERTRES = 117;
        const int DESKTOPHORZRES = 118;
        #endregion

        private bool IsSnap = false;

        private Bitmap ScreenShot;
        public SnapWin(Bitmap bitmap)
        {
            InitializeComponent();
            this.Topmost = true;
            ScreenShot = bitmap;
            MouseDown += Ink_MouseDown;
            MouseUp += Ink_MouseUp;
            MouseMove += Ink_MouseMove;
            MouseRightButtonDown += Ink_MouseRightButtonDown;
            BitmapSource source;
            IsSnap = true;

            try
            {
                source = Imaging.CreateBitmapSourceFromHBitmap(ScreenShot.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
            catch
            {
                source = null;
            }
            ImageBrush b = new ImageBrush();
            b.ImageSource = source;
            b.Stretch = Stretch.Fill;
            Ink.Background = b;
            Ink.EditingMode = InkCanvasEditingMode.None;
            this.WindowStartupLocation = WindowStartupLocation.Manual;
            this.Visibility = Visibility.Visible;
            this.Left = 0;
            this.Top = 0;
            this.Height = SystemParameters.VirtualScreenHeight;
            this.Width = SystemParameters.VirtualScreenWidth;
        }
        
        private bool ISDown = false;                    // 判断是否鼠标左键按下
        System.Windows.Point Startpoint = new System.Windows.Point();
        private void Ink_MouseDown(object sender, MouseButtonEventArgs e)
        {
            ISDown = true;
            Conf.Visibility = Visibility.Collapsed;
            Startpoint = Mouse.GetPosition(this);
        }
        Rect rect;
        /// <summary>
        /// 根据鼠标移动画出矩形标识区域
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Ink_MouseMove(object sender, MouseEventArgs e)
        {
            if (IsSnap)
            {
                if (ISDown == true)
                {
                    System.Windows.Point point = Mouse.GetPosition(this);
                    rect = new Rect(Startpoint, point);
                    Bord.Visibility = Visibility.Visible;
                    Bord.Margin = new Thickness(rect.Left, rect.Top, 0, 0);
                    Bord.Height = rect.Height;
                    Bord.Width = rect.Width;
                }
            }
        }
        Rect snapArea = new Rect();
        private void Ink_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (IsSnap)
            {
                ISDown = false;
                if (Bord.Visibility == Visibility.Collapsed)
                {
                    Conf.Visibility = Visibility.Collapsed;
                }
                else
                {
                    Conf.Visibility = Visibility.Visible;
                }
                if (rect.Width >= 10 && rect.Height >= 10)
                {

                    if (System.Windows.Forms.SystemInformation.VirtualScreen.Height - rect.Bottom >= 0)
                    {
                        snapArea = rect;
                        Conf.Margin = new Thickness(rect.Right - 40, rect.Bottom, 0, 0);

                    }
                    else
                    {
                        snapArea = rect;
                        Conf.Margin = new Thickness(rect.Left + 10, rect.Top + 22, 0, 0);
                    }
                }

            }

        }

        private void Ink_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            ISDown = false;
            Conf.Visibility = Visibility.Collapsed;
            Bord.Visibility = Visibility.Collapsed;

        }

        private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape))
            {
                ISDown = false;
                Conf.Visibility = Visibility.Collapsed;
                Bord.Visibility = Visibility.Collapsed;
                this.Close();
            }
        }
        /// <summary>
        /// 确认区域
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Conf_Click(object sender, RoutedEventArgs e)
        {
            if (IsSnap)
            {
                double dpi = getDpi();
                Rectangle rectangle = new Rectangle((int)((snapArea.X + 2) * dpi), (int)((snapArea.Y + 2) * dpi), (int)((snapArea.Width - 4) * dpi), (int)((snapArea.Height - 4) * dpi));
                var bitmap1 = new Bitmap((int)(snapArea.Width * dpi), (int)(snapArea.Height * dpi), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
               
                using (Graphics g = Graphics.FromImage(bitmap1))
                {
                    g.CopyFromScreen(rectangle.X, rectangle.Y, 0, 0, rectangle.Size, CopyPixelOperation.SourceCopy);
                }
                this.Cursor = Cursors.Wait;
                sendMessage(bitmap1);

                this.Close();
            }


        }

        /// <summary>
        /// 获取系统dpi,这一步很重要
        /// </summary>
        /// <returns></returns>
        double getDpi()
        {
            double dDpi = 1;
            IntPtr desktopDc = GetDC(IntPtr.Zero);
            float horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX);
            float verticalDPI = GetDeviceCaps(desktopDc, LOGPIXELSY);
            int dpi = (int)(horizontalDPI + verticalDPI) / 2;
            dDpi = 1 + ((dpi - 96) / 24) * 0.25;
            if (dDpi < 1)
            {
                dDpi = 1;
            }
            ReleaseDC(IntPtr.Zero, desktopDc);
            return dDpi;
        }
    }
}

  • 窗口创建函数public SnapWin(Bitmap bitmap){...}中使用了参数,所以在创建这个窗口的时候记得加上这个参数,该参数即为获取到的整个屏幕的图像
  • double getDpi(){...}这个函数主要的作用是获取当前屏幕的缩放比,这一点很重要,否则你获得的截图区域可能与你的预期有着很大的区别。(因为屏幕缩放比为200%,导致截图反复失败后获得的经验之谈)
  • 在该程序中如果需要退出截图需要按下ESC键。
  • public delegate void SendMessage(Bitmap bitmap);
    public SendMessage sendMessage;
    是用来实现窗口间传值,将截图区域的图像以sendMessage(截图区域图像);的形式发送到主窗口。
  • 控件选用的是InkCanvas,因为它可以添加墨迹,如果对于这方面感兴趣的,可以学习一下相关内容

主窗口

/// <summary>
        /// 点击截图并识别
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Snap_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = WindowState.Minimized;
            
            SnapWin snapWin = new SnapWin(GetScreen())
            {
                sendMessage = GetSnapArea		// 主窗口接受子窗口发送的值的函数为GetSnapArea
            };
            snapWin.ShowDialog();

            this.WindowState = WindowState.Normal;
            
        }
        
        /// <summary>
        /// 获取截屏区域的文字
        /// </summary>
        /// <param name="bitmap"></param>
        private void GetSnapArea(Bitmap bitmap)
        {
            string str = OCRApi.getFileBase64(bitmap);
            string text = OCRApi.GetWords(str,true);
            string CaText = OCRApi.Str2Json(text);
            this.Visibility = Visibility.Visible;
        }
  • private void GetSnapArea(Bitmap bitmap) {...}函数中,我使用了自己重写的对图像进行文字识别的函数。
		/// <summary>
        /// 转换为用 Base64 数字编码的字符串表示形式(重载)
        /// </summary>
        /// <param name="bitmap">图像文件</param>
        /// <returns>字符串</returns>
        public static String getFileBase64(Bitmap bitmap)
        {
            using (MemoryStream stream = new MemoryStream())

            {
                bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
                byte[] data = new byte[stream.Length];
                stream.Seek(0, SeekOrigin.Begin);
                stream.Read(data, 0, Convert.ToInt32(stream.Length));
                String str = Convert.ToBase64String(data);
                return str;
            }
        }

		/// <summary>
        /// 直接上传base64的字符串,获得内容
        /// </summary>
        /// <param name="base64"></param>
        /// <param name="a">标识符(无明确含义)</param>
        /// <returns></returns>
        public static string GetWords(string base64, bool a)
        {
            string token = getAccessToken();
            string host = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=" + token;
            Encoding encoding = Encoding.Default;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
            request.Method = "post";
            request.KeepAlive = true;
            String str = "image=" + HttpUtility.UrlEncode(base64);
            byte[] buffer = encoding.GetBytes(str);
            request.ContentLength = buffer.Length;
            request.GetRequestStream().Write(buffer, 0, buffer.Length);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string result = reader.ReadToEnd();
            return result;
        }
        

四、结语

从演示中可以看出我已经实现了不少功能,这些功能将在接下来的文章内向大家介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值