-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathScreenViewer.py
173 lines (159 loc) · 6.18 KB
/
ScreenViewer.py
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
import numpy as np
import win32gui
import win32ui, win32con
from threading import Thread, Lock
import time
#DEBUG
from skimage.io import imread
#Asyncronously captures screens of a window. Provides functions for accessing
#the captured screen.
class ScreenViewer:
def __init__(self):
self.mut = Lock()
self.hwnd = None
self.its = None #Time stamp of last image
self.i0 = None #i0 is the latest image;
self.i1 = None #i1 is used as a temporary variable
self.cl = False #Continue looping flag
#Left, Top, Right, and bottom of the screen window
self.l, self.t, self.r, self.b = 0, 0, 0, 0
#Border on left and top to remove
self.bl, self.bt, self.br, self.bb = 12, 31, 12, 20
def GetHWND(self, wname = None):
'''
Gets handle of window to view
wname: Title of window to find
Return: True on success; False on failure
'''
if wname is None:
self.hwnd = win32gui.GetDesktopWindow()
else:
self.hwnd = win32gui.FindWindow(None, wname)
if self.hwnd == 0:
self.hwnd = None
return False
self.l, self.t, self.r, self.b = win32gui.GetWindowRect(self.hwnd)
return True
def GetScreen(self):
'''
Get's the latest image of the window
'''
while self.i0 is None: #Screen hasn't been captured yet
pass
self.mut.acquire()
s = self.i0
self.mut.release()
return s
def GetScreenWithTime(self):
'''
Get's the latest image of the window along with timestamp
'''
while self.i0 is None: #Screen hasn't been captured yet
pass
self.mut.acquire()
s = self.i0
t = self.its
self.mut.release()
return s, t
def GetScreenSize(self):
return (self.b - self.t, self.r - self.l)
def GetScreenTime(self):
'''
Get the timestamp of the last image of screen
'''
while self.i0 is None: #Screen hasn't been captured yet
pass
self.mut.acquire()
s = self.its
self.mut.release()
return s
def GetScreenImg1(self): #DEBUG ONLY
#return imread('Train/Images/20170411195037_1.jpg')[0:-14, 1:-1, :]
#For 800x600 images:
#Remove 12 pixels from bottom 4 from right and left
return imread('TS/Train/Images/20170507141446_1.jpg')[0:-12, 4:-4, :]
def GetScreenImg(self):
'''
Gets the screen of the window referenced by self.hwnd
'''
if self.hwnd is None:
raise Exception("HWND is none. HWND not called or invalid window name provided.")
self.l, self.t, self.r, self.b = win32gui.GetWindowRect(self.hwnd)
#Remove border around window (8 pixels on each side)
#Remove 4 extra pixels from left and right 16 + 8 = 24
w = self.r - self.l - self.br - self.bl
#Remove border on top and bottom (31 on top 8 on bottom)
#Remove 12 extra pixels from bottom 39 + 12 = 51
h = self.b - self.t - self.bt - self.bb
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, w, h)
cDC.SelectObject(dataBitMap)
#First 2 tuples are top-left and bottom-right of destination
#Third tuple is the start position in source
cDC.BitBlt((0,0), (w, h), dcObj, (self.bl, self.bt), win32con.SRCCOPY)
bmInfo = dataBitMap.GetInfo()
im = np.frombuffer(dataBitMap.GetBitmapBits(True), dtype = np.uint8)
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
#Bitmap has 4 channels like: BGRA. Discard Alpha and flip order to RGB
#31 pixels from border on top, 8 on bottom
#8 pixels from border on the left and 8 on right
#Remove 1 additional pixel from left and right so size is 1278 | 9
#Remove 14 additional pixels from bottom so size is 786 | 6
#return im.reshape(bmInfo['bmHeight'], bmInfo['bmWidth'], 4)[31:-22, 9:-9, -2::-1]
#For 800x600 images:
#Remove 12 pixels from bottom + border
#Remove 4 pixels from left and right + border
return im.reshape(bmInfo['bmHeight'], bmInfo['bmWidth'], 4)[:, :, -2::-1]
def GetWindowPos(self):
'''
Gets the left, top, right, and bottom coordinates of the window
'''
return self.l + self.bl, self.t + self.bt, self.r - self.br, self.b - self.bb
def Start(self):
'''
#Begins recording images of the screen
#wf: Write flag; write screen captures to file
'''
#if self.hwnd is None:
# return False
self.cl = True
thrd = Thread(target = self.ScreenUpdateT)
thrd.start()
return True
def Stop(self):
'''
Stop the async thread that is capturing images
'''
self.cl = False
def ScreenUpdateT(self):
'''
Thread used to capture images of screen
'''
while self.cl: #Keep updating screen until terminating
self.i1 = self.GetScreenImg()
self.mut.acquire()
self.i0 = self.i1 #Update the latest image in a thread safe way
self.its = time.time()
self.mut.release()
def WindowDraw(self, rect):
'''
Draws a rectangle to the window
'''
if self.hwnd is None:
return
#raise Exception("HWND is none. HWND not called or invalid window name provided.")
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
#Set background mode to transparent
#dcObj.SetBkColor(0x12345)
#dcObj.SetBkMode(0)
dcObj.Rectangle(rect)
# Free Resources
dcObj.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)