Python利用自帶模塊實(shí)現(xiàn)屏幕像素高效操作
1、獲取屏幕放縮比例
from ctypes import wintypes
import ctypes
HORZRES = 8
LOGPIXELSX = 118
def get_scale_factor() -> float:
user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32
# 定義 HDC 和 UINT 類(lèi)型
HDC = wintypes.HDC
UINT = wintypes.UINT
# 定義 GetDC 和 GetDeviceCaps 的參數(shù)類(lèi)型和返回類(lèi)型
user32.GetDC.argtypes = [wintypes.HWND]
user32.GetDC.restype = HDC
gdi32.GetDeviceCaps.argtypes = [HDC, UINT]
gdi32.GetDeviceCaps.restype = wintypes.INT
# 獲取設(shè)備上下文
dc = user32.GetDC(None)
widthScale = gdi32.GetDeviceCaps(dc, HORZRES)
width = gdi32.GetDeviceCaps(dc, LOGPIXELSX)
scale = width / widthScale
return scale2、獲取屏幕指定坐標(biāo)處像素顏色
import ctypes
from ctypes import wintypes
from typing import Sequence, Generator
user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32
# 定義類(lèi)型
HWND = wintypes.HWND
HDC = wintypes.HDC
HBITMAP = wintypes.HBITMAP
class BITMAPINFOHEADER(ctypes.Structure):
_fields_ = [
("biSize", wintypes.DWORD),
("biWidth", wintypes.LONG),
("biHeight", wintypes.LONG),
("biPlanes", wintypes.WORD),
("biBitCount", wintypes.WORD),
("biCompression", wintypes.DWORD),
("biSizeImage", wintypes.DWORD),
("biXPelsPerMeter", wintypes.LONG),
("biYPelsPerMeter", wintypes.LONG),
("biClrUsed", wintypes.DWORD),
("biClrImportant", wintypes.DWORD)
]
class BITMAPINFO(ctypes.Structure):
_fields_ = [
("bmiHeader", BITMAPINFOHEADER),
("bmiColors", wintypes.DWORD * 3)
]
def get_pixel_color(coords: Sequence[tuple[int, int]], hwnd: HWND) -> Generator[tuple[int, int, int], None, None]:
rect = wintypes.RECT()
user32.GetClientRect(hwnd, ctypes.byref(rect))
width = rect.right - rect.left
height = rect.bottom - rect.top
# 創(chuàng)建內(nèi)存設(shè)備上下文
hdc_src = user32.GetDC(hwnd)
hdc_dst = gdi32.CreateCompatibleDC(hdc_src)
bmp = gdi32.CreateCompatibleBitmap(hdc_src, width, height)
gdi32.SelectObject(hdc_dst, bmp)
# 使用 BitBlt 復(fù)制窗口內(nèi)容到內(nèi)存設(shè)備上下文
gdi32.BitBlt(hdc_dst, 0, 0, width, height, hdc_src, 0, 0, 0x00CC0020) # SRCCOPY
# 獲取位圖信息
bmi = BITMAPINFO()
bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)
bmi.bmiHeader.biWidth = width
bmi.bmiHeader.biHeight = -height # 負(fù)值表示自底向上
bmi.bmiHeader.biPlanes = 1
bmi.bmiHeader.biBitCount = 32
bmi.bmiHeader.biCompression = 0
# 創(chuàng)建緩沖區(qū)并獲取位圖數(shù)據(jù)
buffer = ctypes.create_string_buffer(width * height * 4)
gdi32.GetDIBits(hdc_dst, bmp, 0, height, buffer, ctypes.byref(bmi), 0)
# 釋放資源
gdi32.DeleteObject(bmp)
gdi32.DeleteDC(hdc_dst)
user32.ReleaseDC(hwnd, hdc_src)
# 遍歷指定坐標(biāo)并返回像素顏色
for x, y in coords:
if 0 <= x < width and 0 <= y < height:
offset = (y * width + x) * 4
color = buffer[offset:offset + 4]
yield color[2], color[1], color[0] # BGR -> RGB
else:
yield (0, 0, 0)3、一個(gè)簡(jiǎn)單的使用案例
from typing import Sequence, Generator, Tuple
from tkinter import ttk
import tkinter as tk
from ctypes import wintypes
import ctypes
import requests
from io import BytesIO
from PIL import Image, ImageTk
user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32
HWND = wintypes.HWND
HDC = wintypes.HDC
HBITMAP = wintypes.HBITMAP
class BITMAPINFOHEADER(ctypes.Structure):
_fields_ = [
("biSize", wintypes.DWORD),
("biWidth", wintypes.LONG),
("biHeight", wintypes.LONG),
("biPlanes", wintypes.WORD),
("biBitCount", wintypes.WORD),
("biCompression", wintypes.DWORD),
("biSizeImage", wintypes.DWORD),
("biXPelsPerMeter", wintypes.LONG),
("biYPelsPerMeter", wintypes.LONG),
("biClrUsed", wintypes.DWORD),
("biClrImportant", wintypes.DWORD)
]
class BITMAPINFO(ctypes.Structure):
_fields_ = [
("bmiHeader", BITMAPINFOHEADER),
("bmiColors", wintypes.DWORD * 3)
]
def get_pixel_color(coords: Sequence[Tuple[int, int]], hwnd: HWND) -> Generator[Tuple[int, int, int], None, None]:
rect = wintypes.RECT()
user32.GetClientRect(hwnd, ctypes.byref(rect))
width = rect.right - rect.left
height = rect.bottom - rect.top
hdc_src = user32.GetDC(hwnd)
hdc_dst = gdi32.CreateCompatibleDC(hdc_src)
bmp = gdi32.CreateCompatibleBitmap(hdc_src, width, height)
gdi32.SelectObject(hdc_dst, bmp)
gdi32.BitBlt(hdc_dst, 0, 0, width, height, hdc_src, 0, 0, 0x00CC0020) # SRCCOPY
bmi = BITMAPINFO()
bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)
bmi.bmiHeader.biWidth = width
bmi.bmiHeader.biHeight = -height # 負(fù)值表示自底向上
bmi.bmiHeader.biPlanes = 1
bmi.bmiHeader.biBitCount = 32
bmi.bmiHeader.biCompression = 0
buffer = ctypes.create_string_buffer(width * height * 4)
gdi32.GetDIBits(hdc_dst, bmp, 0, height, buffer, ctypes.byref(bmi), 0)
gdi32.DeleteObject(bmp)
gdi32.DeleteDC(hdc_dst)
user32.ReleaseDC(hwnd, hdc_src)
for x, y in coords:
print(x, y, width, height)
if 0 <= x < width and 0 <= y < height:
offset = (y * width + x) * 4
color = buffer[offset:offset + 4]
yield color[2], color[1], color[0] # BGR -> RGB
else:
yield (0, 0, 0)
def get_window_handle(window):
window_name = window._w
if not window_name.startswith("."):
window_name = "." + window_name
hwnd = ctypes.windll.user32.FindWindowW(None, window.title())
if not hwnd:
raise ValueError("Cannot get the window handle.")
return hwnd
def download_image(url):
response = requests.get(url)
if response.status_code == 200:
return Image.open(BytesIO(response.content))
else:
raise Exception(f"Failed to download image: HTTP {response.status_code}")
def display_image_in_label(image):
photo = ImageTk.PhotoImage(image)
label = ttk.Label(root, image=photo)
label.image = photo # 保持對(duì) PhotoImage 的引用,防止被垃圾回收
label.pack()
def show_color(event):
hwnd = get_window_handle(root)
x, y = event.x, event.y
# 注意這里的坐標(biāo)是相對(duì)于窗口的坐標(biāo),且傳入get_pixel_color的應(yīng)該是包含多個(gè)坐標(biāo)點(diǎn)的序列
# 此外,為了高效獲取同一個(gè)畫(huà)面多個(gè)點(diǎn)的顏色,此處我使用了生成器進(jìn)行懶加載,因此獲取數(shù)據(jù)時(shí)請(qǐng)完整遍歷迭代器
result = get_pixel_color([(x, y)], hwnd)
colors = [i for i in result]
print(f"{event.x, event.y}: {colors}")
if __name__ == "__main__":
root = tk.Tk()
width, height = 900, 500
screenwidth = root.winfo_screenwidth()
screenheight = root.winfo_screenheight()
geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
root.title("測(cè)試樣例")
root.geometry(geometry)
root.bind("<Motion>", show_color)
image_url = "https://ts1.cn.mm.bing.net/th/id/R-C.475631ce281b88c3cd465761b37c5256?rik=ZFMiTYFwaPypTQ&riu=http%3a%2f%2fpic.ntimg.cn%2ffile%2f20180102%2f21532952_215949247000_2.jpg&ehk=9NnCJ9JG44zfdF2%2fr373s25s68H9vxLvyfMsKgEzAwc%3d&risl=&pid=ImgRaw&r=0"
try:
img = download_image(image_url)
display_image_in_label(img)
except Exception as e:
print(f"Error: {e}")
ttk.Label(root, text="Failed to load image.").pack()
root.mainloop()4、總結(jié)
上述方法比通常使用PIL的Image.ImageGrab方法要高效非常多,因?yàn)镮mage.ImageGrab是基于IO截屏操作的,頻繁的IO操作使單純進(jìn)行屏幕像素訪(fǎng)問(wèn)十分低效。
而上述方法采用的是BitBlt。BitBlt 是一種高效的位圖操作方法,可以將窗口的內(nèi)容復(fù)制到內(nèi)存設(shè)備上下文中,然后通過(guò) GetPixel 或直接訪(fǎng)問(wèn)位圖數(shù)據(jù)來(lái)獲取像素顏色。就像素訪(fǎng)問(wèn)而言其性能顯著強(qiáng)于前者。更多關(guān)于Window的API操作詳見(jiàn)官方文檔:
Windows GDI) (位圖函數(shù) - Win32 apps | Microsoft Learn
到此這篇關(guān)于Python利用自帶模塊實(shí)現(xiàn)屏幕像素高效操作的文章就介紹到這了,更多相關(guān)Python屏幕像素操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PYQT5 vscode聯(lián)合操作qtdesigner的方法
這篇文章主要介紹了PYQT5 vscode聯(lián)合操作qtdesigner的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
報(bào)錯(cuò)解決:Anaconda虛擬環(huán)境下使用uwsgi運(yùn)行uwsgi.ini報(bào)錯(cuò)ImportError問(wèn)題
作者通過(guò)安裝和運(yùn)行uwsgi部署flask項(xiàng)目時(shí)遇到了各種問(wèn)題,最后通過(guò)修改uwsgi配置文件、卸載并重新安裝uwsgi以及使用conda安裝uwsgi解決了問(wèn)題2026-02-02
如何利用pandas將Excel轉(zhuǎn)為html格式
工作中經(jīng)常會(huì)遇到Excel轉(zhuǎn)為html格式這種需求,下面這篇文章主要給大家介紹了關(guān)于如何利用pandas將Excel轉(zhuǎn)為html格式的相關(guān)資料,主要利用的是pd.to_html,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-08-08
Python實(shí)現(xiàn)FTP上傳文件或文件夾實(shí)例(遞歸)
本篇文章主要介紹了Python實(shí)現(xiàn)FTP上傳文件或文件夾實(shí)例(遞歸),具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
TensorFlow索引與切片的實(shí)現(xiàn)方法
這篇文章主要介紹了TensorFlow索引與切片的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

