티스토리 뷰
======================================================
안녕하세요 Doridori 입니다.
지난번에 도리도리 하고 싶은거 다해에 자동화 공장에 대한 내용을 만드느라 용을 많이 쓰고
회사 업무가 너무 정신없어져서 뭔가에 집중을 하지 못하고 있는 요즘입니다.
예전에 일을 하면서 잠도 못자고 몸의 피로가 극에 달할 때가 있었는데 요즘은 몸이 많이 피로하고 이런건 아닌데 여러가지 많은 업무를 진행 하다 보니 한가지에 집중도 못하고 정신적으로 매우 지치는 상태에 들어간것 같습니다.
이러고 있는 시기에 지인분이 이런 프로그램을 만들어 보고 싶다고 하셔서 어떤 느낌으로 개발 하면 될까 싶어서 한번 견적을 내 보다가 그냥 만들게 되었습니다. =0=a
연말 까지 일정들이 빡빡하게 잡혀있어서 뭔가 하고 싶은걸 할 여유가 없는데 이런 것들을 하면서 마음의 여유가 생긴다고 하면 어떻게 생각들 하실까 싶네요 ㅋㅋ
자주 업데이트를 하지는 못하지만 업데이트 되면 와서 봐주시는 분들 감사드립니다. ^^
모두 화이팅 하십시오!!
======================================================
64. 광학문자인식 (Screenshot & OCR)
UI)
Soruce) (OCR API의 경우 용량이 많아서 제외하고 올렸습니다. Nuget에서 IRON OCR 추가 해서 사용하시면 됩니다.)
교재)
강의를 준비하려고 하면 주제를 정하고 그 주제에 대해 잘못된 내용을 말씀 드리면 안되니 제가 아는 부분과 잘못 알고 있는 부분들을 다 찾아서 내용들을 정리하고, 그 뒤에 예제를 고민해서 만들고 만든 내용들을 정리하고 하면 제법 많은 시간이 걸리는데, 회사 업무가 바쁘다 보니 주제를 정리 하는데 많은 시간이 걸렸습니다.
그러던 차에 지인 분이 해보고 싶다고 한 부분이 있어서 테스트 해보다가 하나 추가 하게 되었습니다.
OCR(Optical Character Recognition) (광학 문자 인식)의 경우 예전에는 어느정도 전문 분야라고 생각 되었으나 e-book이나 증강현실같은 분야들이 올라오면서 어느정도 사용하기 편하게 되어 있는 API들이 많아 진듯 합니다.
API들을 보면 용량도 많고 Data Pack같은 것들이 있는걸 보면 학습이라던지 폰트 등의 개념도 들어가는것이 아닌가 생각 됩니다.
깊게 보면 재미있을듯 하기도 하지만 일단 사용하라고 만들어 놓은 API가 있으니 간단하게 써보겠습니다.
UI)
간단하게 스크린샷 찍는 부분과 찍은 스크린 샷에서 문자를 인식 하는 부분으로 구성 하였습니다.
강의)
간만에 하는 부분이기도 하고 뭔가 준비를 빡빡하게 하지 못한 부분이 있어서 버벅이기도 하고 흐름이 매끄럽지 않은데, 제 강의 중에 길지 않은 편이니 예제 하나 건졌다고 생각하고 가볍게 봐주셨으면 좋겠습니다.
소스를 붙여넣으면서 설명하는 형태로 만들었기 때문에 건너뛰는 부분들은 예제를 받아서 보시거나 소스 부분을 긁어다 사용 하시면 좋을 듯 합니다.
Source)
Form1)
- Main Form 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using IronOcr;
namespace ImageCapture
{
public partial class Form1 : Form
{
#region 전역
frmSelector selector; // Capture Size 정의를 위한 selector 호출
#endregion
/// <summary>
/// Program 시작
/// </summary>
public Form1()
{
InitializeComponent();
// IronOcr.Installation.LicenseKey = "IRONOCR-MYLICENSE-KEY-1EF01"; // IromOcr License 사용을 위해 추가 할 필요가 있을 듯 . . . (테스트는 상관 없다만. . . )
}
#region Form Event
/// <summary>
/// Form Load Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
// selector Popup 정의 및 Delegate Event 등록
selector = new frmSelector();
selector.eScreenInfoSander += selector_eScreenInfoSander; // selector Form에서 Size를 가져온뒤에 호출 되는 Event
}
/// <summary>
/// Capture Button Click Event
/// > Size를 가져오기 위한 selector Form 호출
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnCapture_Click(object sender, EventArgs e)
{
selector.ShowInTaskbar = false;
selector.FrameBorderSize = 8;
selector.FrameColor = Color.LightGreen;
selector.AllowedTransform = true;
selector.Show();
}
/// <summary>
/// selector Form에서 Size를 가져온뒤에 호출 되는 Event (Delegate Event)
/// </summary>
/// <param name="oSender"></param>
/// <param name="dInfo"></param>
private void selector_eScreenInfoSander(object oSender, Dictionary<string, int> dInfo)
{
selector.Opacity = 0; // Image Capture 하는 시점에 Size를 가져오는 Form을 숨기기 위해 사용 (Capture 후 다시 보이는 부분이 필요해서 한곳에서 제어하기 위해서 부모 From에서 제어)
pboxScreen.Image = imgCapture(dInfo["X"], dInfo["Y"], dInfo["W"], dInfo["H"]);
selector.Opacity = 100; // Image Capture 후 다시 보이게 함
}
/// <summary>
/// OCR Button Click Event
/// Picture Box에 있는 Image에 있는 OCR Data 추출
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOCR_Click(object sender, EventArgs e)
{
Bitmap oc = (Bitmap)pboxScreen.Image;
var Ocr = new IronTesseract();
Ocr.Language = (cboxKorea.Checked) ? OcrLanguage.Korean : OcrLanguage.English; // 설정 값이고 코드가 기니까 3항식으로
using (var Input = new OcrInput(oc))
{
var Result = Ocr.Read(Input);
tboxOCR.Text = Result.Text;
}
}
#endregion
#region Inner Function
/// <summary>
/// 이미지 캡쳐 하는 부분
/// Window 화면 비율 기준으로 크기만 맞추기 때문에 듀얼 스크린 사용 시 추가 개발 필요
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="w"></param>
/// <param name="h"></param>
/// <returns></returns>
private Bitmap imgCapture(int x, int y, int w, int h)
{
double dRef = GetScreenScalingFactor(); // 화면 비율을 찾아서 맞춰서 크기를 변환 함
int scPositionX = (int)(x * dRef);
int scPositionY= (int)(y * dRef);
int scWidth= (int)(w * dRef);
int scHeight= (int)(h * dRef);
Bitmap bt = new Bitmap(scWidth, scHeight);
using (Graphics g = Graphics.FromImage(bt)) // using : 자원을 사용한 뒤 dispose 하지 않아도 알아서 자원을 해제
{
g.CopyFromScreen(scPositionX, scPositionY, 0, 0, bt.Size, CopyPixelOperation.SourceCopy);
}
// 이미지 캡쳐 좌표점을 표시해주기 위해 사용
tboxSize.Text = $@"x:{scPositionX}, y:{scPositionY}, w:{scWidth}, h:{scHeight}";
return bt; //imgResize(bt);
}
/// <summary>
/// Image Size 변경을 위해 사용 (굳이 변경하지 않아도...)
/// </summary>
/// <param name="orgImg"></param>
/// <returns></returns>
private Bitmap imgResize(Bitmap orgImg)
{
int width = (int)(orgImg.Width );
int height = (int)(orgImg.Height);
Size resize = new Size(width, height);
return new Bitmap(orgImg, resize);
}
#endregion
#region Window 배율 관련 Function (별도 관리)
/// <summary>
/// 장치 정보 구하기
/// </summary>
/// <param name="deviceContextHandle">디바이스 컨텍스트 핸들</param>
/// <param name="index">인덱스</param>
/// <returns>장치 정보</returns>
[System.Runtime.InteropServices.DllImport("gdi32")]
private static extern int GetDeviceCaps(IntPtr deviceContextHandle, int index);
/// <summary>
/// 화면 배율 계수 설정하기
/// </summary>
/// <returns>배율 계수</returns>
public float GetScreenScalingFactor()
{
Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
IntPtr deviceHandle = graphics.GetHdc();
int PhysicalScreenHeight = GetDeviceCaps(deviceHandle, 117);
//int LogicalScreenHeight = GetDeviceCaps(deviceHandle, 10); // 배율 값이 맞지 않아 다른 방식으로 구했는데 뭘 잘 못한건가?
Size sizeNext = Screen.PrimaryScreen.Bounds.Size;
int LogicalScreenHeight = sizeNext.Height;
float scalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
return scalingFactor; // 1.25 = 125%
}
#endregion
}
}
|
cs |
frmSelector)
- 스크린샷을 찍을 영역의 좌표를 가져오는 Popup Form에 대한 코드 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace ImageCapture
{
/// <summary>
/// Screen Size Select
/// </summary>
public partial class frmSelector : Form
{
#region From Size Change Sample
private static readonly Color PrimaryTransparencyKey = Color.White;
private static readonly Color SecondaryTransparencyKey = Color.Black;
public Rectangle SelectedWindow
{
get
{
var rect = this.Bounds;
rect.Inflate(_FrameBorderSize * -1, _FrameBorderSize * -1);
return rect;
}
}
private Color _FrameColor = Color.Red;
[DefaultValue(typeof(Color), "Red")]
public Color FrameColor
{
get { return _FrameColor; }
set
{
_FrameColor = value;
if (_FrameColor == PrimaryTransparencyKey)
this.TransparencyKey = SecondaryTransparencyKey;
else
this.TransparencyKey = PrimaryTransparencyKey;
this.Refresh();
}
}
private int _FrameBorderSize = 5;
[DefaultValue(5)]
public int FrameBorderSize
{
get { return _FrameBorderSize; }
set
{
_FrameBorderSize = value;
this.Refresh();
}
}
[DefaultValue(true)]
public bool AllowedTransform { get; set; } = true;
Point mousePoint;
public frmSelector()
{
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
this.TransparencyKey = PrimaryTransparencyKey;
this.TopMost = true;
}
void draw(Graphics g)
{
var rct = this.ClientRectangle;
g.FillRectangle(new SolidBrush(FrameColor), rct);
rct.Inflate(FrameBorderSize * -1, FrameBorderSize * -1);
g.FillRectangle(new SolidBrush(TransparencyKey), rct);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
mousePoint = new Point(e.X, e.Y);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (AllowedTransform)
{
if (e.X > Width * 2 / 3 && e.Y > Height * 1 / 2)
frameTransform(e);
else
frameMove(e);
}
else
frameMove(e);
base.OnMouseMove(e);
}
void frameMove(MouseEventArgs e)
{
Cursor = Cursors.SizeAll;
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
this.Left += e.X - mousePoint.X;
this.Top += e.Y - mousePoint.Y;
}
}
void frameTransform(MouseEventArgs e)
{
if (AllowedTransform)
{
Cursor = Cursors.SizeNWSE;
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
this.Width += e.X - mousePoint.X;
mousePoint.X += e.X - mousePoint.X;
this.Height += e.Y - mousePoint.Y;
mousePoint.Y += e.Y - mousePoint.Y;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
draw(e.Graphics);
base.OnPaint(e);
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
this.Refresh();
}
#endregion
#region Form Size 전송을 위한 추가 Code
public delegate void delScreenInfoSender(object oSender, Dictionary<string, int> dInfo);
public event delScreenInfoSender eScreenInfoSander;
/// <summary>
/// Form Double Click 시 해당 위치의 Size를 가져 옴
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmSelector_DoubleClick(object sender, EventArgs e)
{
//int x = this.Location.X;
//int y = this.Location.Y;
//int w = this.Width;
//int h = this.Height;
eScreenInfoSander(this, fCaptureInfo());
this.Hide();
}
/// <summary>
/// Screen Size Convert Dictionary
/// </summary>
/// <returns></returns>
public Dictionary<string, int> fCaptureInfo()
{
Dictionary<string, int> Info = new Dictionary<string, int>();
Info.Add("X", this.Location.X);
Info.Add("Y", this.Location.Y);
Info.Add("W", this.Width);
Info.Add("H", this.Height);
return Info;
}
#endregion
}
}
|
cs |
'C# > C# 강좌 Winform' 카테고리의 다른 글
Doridori C# 강의 3) 66. Software Design Pattern MVP (feat.MVC) (4) | 2023.01.28 |
---|---|
Doridori C# 강의 3) 65. Logging (log4net) (0) | 2022.11.24 |
Doridori C# 강의 3) 63. 메일 보내기 2 (DB send email) (0) | 2022.08.01 |
Doridori C# 강의 3) 62. 메일 보내기 (send email) (17) | 2022.06.19 |
Doridori C# 강의 3) 61. 정규식 (Regular Expression) (4) | 2022.05.06 |
- Total
- Today
- Yesterday
- 도리도리 C#
- C# 강의
- 위스키
- C#
- 모바일
- 나들이
- 게임
- 술
- 가볼만한곳
- 소주
- Visual Studio
- 증류주
- 맛집
- 프로그램 강의
- 보드카
- 맥주
- 서울
- 칵테일
- 하이볼
- 전통주
- 광저우
- WinForm
- 주말
- 도리도리
- C# 기초 강의
- 아이랑
- 가볼만한 곳
- C# 강좌
- 막걸리
- 와인
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |