diff --git a/CaptureWindow.xaml b/CaptureWindow.xaml new file mode 100644 index 0000000..31ab39d --- /dev/null +++ b/CaptureWindow.xaml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + diff --git a/CaptureWindow.xaml.cs b/CaptureWindow.xaml.cs new file mode 100644 index 0000000..f21657d --- /dev/null +++ b/CaptureWindow.xaml.cs @@ -0,0 +1,225 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; + + +namespace ScreenShotOCR +{ + /// + /// CaptureWindow.xaml에 대한 상호 작용 논리 + /// + public partial class CaptureWindow : Window + { + public EventHandlerGrab m_handlerGrab = null; + + Bitmap bitmap = null; + Bitmap crop = null; + Image bg = null; + + int screen_w = (int)SystemParameters.PrimaryScreenWidth; + int screen_h = (int)SystemParameters.PrimaryScreenHeight; + System.Drawing.Rectangle roi = new System.Drawing.Rectangle(); + float resX = 1.0f; + float resY = 1.0f; + bool bClicked = false; + System.Drawing.PointF pt = new System.Drawing.PointF(); + + + public CaptureWindow() + { + InitializeComponent(); + SetTransparent(true); + + m_handlerGrab = delegate () + { + var crop = bitmap.Clone(roi, bitmap.PixelFormat); + return crop; + }; + } + + public void SetTransparent(bool enable) + { + if (enable) + { + this.Background = System.Windows.Media.Brushes.Transparent; + ScreenView.Source = null; + } + else + { + this.Background = System.Windows.Media.Brushes.White; + } + } + + private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + this.DragMove(); + } + + public void copyBtn_Click(object sender, RoutedEventArgs e) + { + try + { + //new Task(delegate + //{ + SetTransparent(false); + if (bitmap != null) bitmap.Dispose(); + if (bg != null) { bg.Dispose(); } + bitmap = new Bitmap(screen_w, screen_h, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + var gr = Graphics.FromImage(bitmap); + gr.CopyFromScreen(0, 0, 0, 0, bitmap.Size); + string name = "snapshot.png"; + bg = ResizeImage(bitmap, new System.Drawing.Size(screen_w, screen_h)); + ScreenView.Source = ConvertToImageSource(bg); + bitmap.Save(name, ImageFormat.Png); + gr.Dispose(); + //}, TaskCreationOptions.DenyChildAttach).Start(); + } + catch (Exception ex) + { + + } + } + + public static Bitmap ResizeImage(Bitmap imgToResize, System.Drawing.Size size) + { + try + { + Bitmap b = new Bitmap(size.Width, size.Height); + using (Graphics g = Graphics.FromImage((Image)b)) + { + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + g.DrawImage(imgToResize, 0, 0, size.Width, size.Height); + } + return b; + } + catch + { + Console.WriteLine("Bitmap could not be resized"); + return imgToResize; + } + } + + private ImageSource ConvertToImageSource(Image image) + { + using (var ms = new MemoryStream()) + { + image.Save(ms, ImageFormat.Bmp); + ms.Seek(0, SeekOrigin.Begin); + + var bitmapImage = new BitmapImage(); + bitmapImage.BeginInit(); + bitmapImage.CacheOption = BitmapCacheOption.OnLoad; + bitmapImage.StreamSource = ms; + bitmapImage.EndInit(); + + return bitmapImage; + } + } + + private void ClearBtn_Click(object sender, RoutedEventArgs e) + { + SetTransparent(true); + } + + private void Close_Click(object sender, RoutedEventArgs e) + { + this.Visibility = Visibility.Hidden; + } + + private void ScreenView_MouseDown(object sender, MouseButtonEventArgs e) + { + if (!bClicked) + { + bClicked = true; + var p = e.GetPosition(ScreenView); + float x = Convert.ToSingle(Math.Round(p.X * resX)); + float y = Convert.ToSingle(Math.Round(p.Y * resY)); + pt = new System.Drawing.PointF(x, y); + roi.X = Convert.ToInt32(x); + roi.Y = Convert.ToInt32(y); + } + } + + private void ScreenView_MouseMove(object sender, MouseEventArgs e) + { + if (bClicked) + { + var p = e.GetPosition(ScreenView); + float x = Convert.ToSingle(p.X); + float y = Convert.ToSingle(p.Y); + var w = (float)Math.Round(x * resX - pt.X); + var h = (float)Math.Round(y * resY - pt.Y); + roi.Width = Convert.ToInt32(w); + roi.Height = Convert.ToInt32(h); + CheckROI(); + DrawROI(); + } + } + + private void ScreenView_MouseUp(object sender, MouseButtonEventArgs e) + { + if (bClicked) + { + var p = e.GetPosition(ScreenView); + float x = Convert.ToSingle(p.X); + float y = Convert.ToSingle(p.Y); + var w = (float)Math.Round(x * resX - pt.X); + var h = (float)Math.Round(y * resY - pt.Y); + roi.Width = Convert.ToInt32(w); + roi.Height = Convert.ToInt32(h); + CheckROI(); + DrawROI(); + bClicked = false; + } + } + + private void CheckROI() + { + int x = roi.X; + if (x < 0) { x = 0; } else if (x >= screen_w) { x = screen_w - 1; } + int y = roi.Y; + if (y < 0) { y = 0; } else if (y >= screen_h) { y = screen_h - 1; } + int w = roi.Width; + if (w < 0) { w = 0; } else if (x + w >= screen_w) { w = screen_w - 1 - x; } + int h = roi.Height; + if (h < 0) { h = 0; } else if (y + h >= screen_h) { h = screen_h - 1 - y; } + roi = new System.Drawing.Rectangle(x, y, w, h); + } + + private void DrawROI() + { + if (bg == null) return; + try + { + int x = (int)Math.Round(Convert.ToSingle(roi.X / resX)); + int y = (int)Math.Round(Convert.ToSingle(roi.Y / resY)); + int w = (int)Math.Round(Convert.ToSingle(roi.Width / resX)); + int h = (int)Math.Round(Convert.ToSingle(roi.Height / resY)); + + System.Drawing.Pen pen = new System.Drawing.Pen(System.Drawing.Color.Red, 1); + System.Drawing.Rectangle rect = new System.Drawing.Rectangle(x, y, w, h); + var tmp = (Bitmap)bg.Clone(); + var gr = Graphics.FromImage(tmp); + gr.DrawRectangle(pen, rect); + ScreenView.Source = ConvertToImageSource(tmp); + + //posX.Text = roi.X.ToString(); + //posY.Text = roi.Y.ToString(); + //posW.Text = roi.Width.ToString(); + //posH.Text = roi.Height.ToString(); + + gr.Dispose(); + tmp.Dispose(); + } + catch + { + } + } + + } +} diff --git a/MainWindow.xaml b/MainWindow.xaml index 87dd8bd..604a2a7 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -13,7 +13,7 @@ - - - - - - - + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index a6b5658..78d20d4 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -1,25 +1,21 @@ 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.Navigation; -using System.Windows.Shapes; +using System.Drawing; +using System.IO; +using System.Drawing.Imaging; +using PINBlog; namespace ScreenShotOCR { + public delegate Bitmap EventHandlerGrab(); /// /// MainWindow.xaml에 대한 상호 작용 논리 /// public partial class MainWindow : Window { + CaptureWindow m_cw = new CaptureWindow(); + public MainWindow() { InitializeComponent(); @@ -29,5 +25,42 @@ namespace ScreenShotOCR { this.DragMove(); } + + private void CaptureBtn_Click(object sender, RoutedEventArgs e) + { + m_cw.Show(); + this.Topmost = true; + } + + private void Close_Click(object sender, RoutedEventArgs e) + { + m_cw.Close(); + this.Close(); + } + + private void OCRBtn_Click(object sender, RoutedEventArgs e) + { + Bitmap crop = m_cw.m_handlerGrab(); + Tesseract(crop); + } + + private bool Tesseract(Image image) + { + try + { + MemoryStream stream = new MemoryStream(); + image.Save(stream, ImageFormat.Png); + var tesseract = new Tesseract(); + var result = tesseract.GetText(stream); + textblock.Text = result; + return true; + } + catch (Exception ex) + { + return false; + } + } + + } } diff --git a/ScreenShotOCR.csproj b/ScreenShotOCR.csproj index 10f00ab..b624c79 100644 --- a/ScreenShotOCR.csproj +++ b/ScreenShotOCR.csproj @@ -37,6 +37,10 @@ + + + packages\System.Drawing.Common.7.0.0\lib\net462\System.Drawing.Common.dll + @@ -55,6 +59,14 @@ MSBuild:Compile Designer + + CaptureWindow.xaml + + + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -86,13 +98,18 @@ ResXFileCodeGenerator Resources.Designer.cs + SettingsSingleFileGenerator Settings.Designer.cs + + + + \ No newline at end of file diff --git a/Tesseract.cs b/Tesseract.cs new file mode 100644 index 0000000..115d51b --- /dev/null +++ b/Tesseract.cs @@ -0,0 +1,169 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text; + +namespace PINBlog +{ + public enum LANGUAGE { ENG = 0 } + + public class OCRResultInfo + { + public LANGUAGE Language { get; set; } + public string Result { get; set; } + + public OCRResultInfo() + { + Language = LANGUAGE.ENG; + Result = ""; + } + } + + public class Tesseract + { + private string m_TesseractExePath; + private LANGUAGE m_Language; + + /// + /// Initializes a new instance of the class. + /// + /// The path for the Tesseract4 installation folder (C:\Program Files\Tesseract-OCR). + /// The language used to extract text from images (eng, por, etc) + /// The data with the trained models (tessdata). Download the models from https://github.com/tesseract-ocr/tessdata_fast + public Tesseract(LANGUAGE language = LANGUAGE.ENG) + { + // Tesseract configs. + var dir = Path.Combine(".", "tessdata"); + m_TesseractExePath = Path.Combine(dir,"tesseract.exe"); + m_Language = language; + Environment.SetEnvironmentVariable("TESSDATA_PREFIX", dir); + } + + public Stream ToStream(Image image, ImageFormat format = null) + { + var stream = new System.IO.MemoryStream(); + if (format == null) + { + image.Save(stream, image.RawFormat); + } + else + { + image.Save(stream, format); + } + stream.Position = 0; + return stream; + } + + /// + /// Read text from the images streams. + /// + /// The images streams. + /// The images text. + public string GetText(params Stream[] images) + { + var output = string.Empty; + + if (images.Any()) + { + var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + var tempInputFile = NewTempFileName(tempPath); + var tempOutputFile = NewTempFileName(tempPath); + + try + { + WriteInputFiles(images, tempPath, tempInputFile); + + var info = new ProcessStartInfo + { + FileName = m_TesseractExePath, + Arguments = $"{tempInputFile} {tempOutputFile} -l {m_Language.ToString()}", + RedirectStandardError = true, + RedirectStandardOutput = true, + CreateNoWindow = true, + UseShellExecute = false + }; + + using (var ps = Process.Start(info)) + { + ps.WaitForExit(); + + var exitCode = ps.ExitCode; + + if (exitCode == 0) + { + output = File.ReadAllText(tempOutputFile + ".txt"); + + var charset = new char[] { ' ', '\n', '\f' }; + output = output.TrimStart(charset).TrimEnd(charset); + } + else + { + var stderr = ps.StandardError.ReadToEnd(); + throw new InvalidOperationException(stderr); + } + } + } + finally + { + Directory.Delete(tempPath, true); + } + } + + return output; + } + + private void WriteInputFiles(Stream[] inputStreams, string tempPath, string tempInputFile) + { + // If there is more thant one image file, so build the list file using the images as input files. + if (inputStreams.Length > 1) + { + var imagesListFileContent = new StringBuilder(); + + foreach (var inputStream in inputStreams) + { + var imageFile = NewTempFileName(tempPath); + + using (var tempStream = File.OpenWrite(imageFile)) + { + CopyStream(inputStream, tempStream); + } + + imagesListFileContent.AppendLine(imageFile); + } + + File.WriteAllText(tempInputFile, imagesListFileContent.ToString()); + } + else + { + // If is only one image file, than use the image file as input file. + using (var tempStream = File.OpenWrite(tempInputFile)) + { + CopyStream(inputStreams.First(), tempStream); + } + } + } + + private void CopyStream(Stream input, Stream output) + { + if (input.CanSeek) + input.Seek(0, SeekOrigin.Begin); + + //input.CopyTo(output); + + Byte[] buffer = new byte[input.Length]; + int len = input.Read(buffer, 0, buffer.Length); + output.Write(buffer, 0, len); + + input.Close(); + } + + private string NewTempFileName(string tempPath) + { + return Path.Combine(tempPath, Guid.NewGuid().ToString()); + } + } +} diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..205824a --- /dev/null +++ b/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tessdata/eng.traineddata b/tessdata/eng.traineddata new file mode 100644 index 0000000..bbef467 Binary files /dev/null and b/tessdata/eng.traineddata differ diff --git a/tessdata/tesseract.exe b/tessdata/tesseract.exe new file mode 100644 index 0000000..d37ef5d Binary files /dev/null and b/tessdata/tesseract.exe differ