PIL's ImageGrab is capturing at the wrong resolution

Question:

I’m trying to get a full screen (1920 x 1080) capture using this code.
The saved images are only 1536 x 864 though.

solution: As Mark pointed out below, Windows has scaling which can be changed via Control Panel > Display (turn it all the way down).

from PIL import ImageGrab
import os
import time

def screenGrab():
    # snapshot of screen
    im = ImageGrab.grab()
    # saves in current work directory with name based on time of pic
    im.save(os.getcwd() + '\full_snap__' + str(int(time.time()))
            + '.png', 'PNG')

def main():
    screenGrab()

if __name__ == '__main__':
    main()
Asked By: NoahLE

||

Answers:

If you have your Display settings set to anything other than the “smaller” (100%) setting which is the default, Windows will tell your applications to render to a smaller area and then magnify the results as it puts it on the desktop. Evidently PIL has a bug caused by this setting, the capture is being cropped to the smaller size rather than the full desktop. The workaround is to be sure that your display settings are set to 100%.

Answered By: Mark Ransom

I manage to overcome this issue by adding registry key at

HKEY_CURRENT_USERSoftwareMicrosoftWindows NTCurrentVersionAppCompatFlagsLayers

add a key with the path to your python.exe and pythonw.exe and in value set HIGHDPIAWARE

like so:

"C:UsersGregAnaconda3python.exe"="HIGHDPIAWARE"

"C:UsersGregAnaconda3pythonw.exe"="HIGHDPIAWARE"

then everythings should be ok 🙂

credit to that post: Marking Your Python Program as High DPI Aware Seamlessly Windows

Answered By: Gregy8

I have faced the same issue.. running on 4k screen trying to capture a 1080p app. Thanks to this thread, call the following code before ImageGrab:

from ctypes import windll
user32 = windll.user32
user32.SetProcessDPIAware()

window_size = get_window_info()
last_time = time.time()
cv2.namedWindow("output", cv2.WINDOW_NORMAL)
while True:
    screen = np.array(ImageGrab.grab(bbox=window_size))
    # print('Frame took {} seconds'.format(time.time()-last_time))
    last_time = time.time()
    # new_screen = process_img(screen)
    imS = cv2.resize(screen, (960, 540))
    cv2.imshow('output', imS)
    # cv2.imshow('window',cv2.cvtColor(screen, cv2.COLOR_BGR2RGB))
    if cv2.waitKey(25) & 0xFF == ord('q'):
        cv2.destroyAllWindows()
        break

This solved my problem.

Answered By: Yikang Luo