2 Replies - 1385 Views - Last Post: 29 April 2012 - 03:07 AM Rate Topic: -----

#1 russelluke  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 22-April 12

ctypes, windll: How to use the same image after screen capture?

Posted 22 April 2012 - 04:45 AM

Hi to everyone here, since this is my first post(apologies if this sentence is innapropriate :))

The code below is supposed to find the co-ordinates of a button on the screen. It runs but it's way too slow. It takes almost a minute for each iteration of x.

I'm assuming it's because, in the algorithm that goes through each row and column(or x and y), in the find_show_button() function, it recaptures the entire screen over every iteration. Some googling also suggests that screen capturing is usually slow. So how do I make the algo capture one image and use that same image until a result is found?

Although I may be wrong so any other insight are also appreciated.

from ctypes import windll

im = windll.user32.GetDC(0)

def getpix(x,y):
    return windll.gdi32.GetPixel(im,x,y)

def find_show_button():
    xy = []
    for x in range(1680):
        for y in range(1050):
            if getpix(x, y) == 1343713 and getpix(x+134, y) == 1343713:
                xy.append(x+67)
                xy.append(y)
                break
        if len(xy) == 2:
            return xy
    return False


Is This A Good Question/Topic? 0
  • +

Replies To: ctypes, windll: How to use the same image after screen capture?

#2 Motoma  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 452
  • View blog
  • Posts: 796
  • Joined: 08-June 10

Re: ctypes, windll: How to use the same image after screen capture?

Posted 22 April 2012 - 07:46 AM

As you have discovered, GDI calls are very slow. There are various techniques for capturing screenshots using GDI--here's one example--if you convert the code from one of them into ctypes, you should then be able to iterate over the resulting image with PIL.
Was This Post Helpful? 0
  • +
  • -

#3 russelluke  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 22-April 12

Re: ctypes, windll: How to use the same image after screen capture?

Posted 29 April 2012 - 03:07 AM

Thanks Motoma, I took your advice but I'm afraid I still have too much to learn about programming and python. So in the end, while trying desperately to avoid doing this, I simply settled for the solution of saving the image to the hard drive and then loading it again with PIL. I thought that would create a big bottleneck but it didn't it actually works quite well, well enough for my purposes so far at least. It may need to be reworked come time when it's used for something like a game bot, if I ever get there.

Solution:
I used the wx module to save the screenshot to the drive since other modules such as ImageGrab don't seem to capture the entire screen. That was a whole other problem for another day but it seems when ImageGrab.grab() is called even if the bbox parameter is specified, it doesn't go beyond 1120x700 on my screen so the rest of the image is just black.

After saving with wx, PIL then loads the image and creates a list of all the RGB pixel values in the image where each pixel takes the form of a tuple e.g. (255,255,255). So the list looks like shot = [(3,23,255), (0,0,0), ...]. This is done for the screenshot and the image that needs to be found in the screenshot.

Using a little arithmetic and simple list operations like list.find() the exact center coordinates of the first instance of the image in the screenshot are returned as [x,y] or False if not found.

It all computes in about a second or less when using python2.7-64bit on a core2quad 2.6GHz, although it may vary depending. With python2.6 32bit it takes almost three seconds. So it's not perfect.

import wx, Image
from math import ceil


def find(match='match.bmp'):
    '''
    Given a 24bit bmp image -> match, which must already be saved in the same path manually,
    find(match) will return a list [x, y] of coordinates of the center of the image match in
    in the screenshot image,
    or False if not found.

    match -> string -> name of image without the '.bmp' extension
    i.e. can only accept bmp.
    '''
    app = wx.App(False)
    screen = wx.ScreenDC()
    size = (1680,1050)
    bmp = wx.EmptyBitmap(size[0], size[1], depth=24)
    mem = wx.MemoryDC(bmp)
    mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
    del mem
    bmp.SaveFile('screenshot.bmp', wx.BITMAP_TYPE_BMP)


    ### load screenshot.bmp pixels
    name = 'screenshot.bmp'
    im = Image.open(name)
    shot = im.load()


    ### load match.bmp pixels
    name = match + '.bmp'
    im = Image.open(name)
    match = im.load()



    ### find max x and y values for match
    temp = ''
    max_pixels_x = 0
    max_pixels_y = 0
    for x in range(10000):
        try:
            temp = match[x,0]
        except IndexError:
            max_pixels_x = x
            break
    for y in range(10000):
        try:
            temp = match[0,y]
        except IndexError:
            max_pixels_y = y
            break
    match_width = max_pixels_x
    match_height = max_pixels_y

    ### create lists of pixels for match_l
    match_l = []
    for y in range(max_pixels_y):
        for x in range(max_pixels_x):
            match_l.append(match[x,y])


    ### find max x and y values for shot
    temp = ''
    max_pixels_x = 0
    max_pixels_y = 0
    for x in range(10000):
        try:
            temp = shot[x,0]
        except IndexError:
            max_pixels_x = x
            break
    for y in range(10000):
        try:
            temp = shot[0,y]
        except IndexError:
            max_pixels_y = y
            break
    shot_width = max_pixels_x


    ### create lists of pixels for shot_l
    shot_l = []
    for y in range(max_pixels_y):
        for x in range(max_pixels_x):
            shot_l.append(shot[x,y])


    ### remove null color from match
##    null = (181, 230, 29)
##    match_l = [x for x in match_l if x != null]


    ### find index of match in shot
    if match_l[0] in shot_l:
        i = -1
        result = True
        run = True
        while run == True:
            result = True   # assume match will be found
            run = False     #   ''    ''    ''  ''   ''
            try:
                i = shot_l.index(match_l[0], i+1)
            except ValueError:
                result = False
                run = False
                break
            
            # test for match in shot starting from index i in shot
            for y in range(match_height):
                for x in range(match_width):
                    if match_l[x + (match_width * y)] != shot_l[i + (shot_width * y) + x]:
                        result = False
                        run = True
                        break

        if result == True:

            ### find coords
            y = int(ceil(float(i)/shot_width)) + (match_height/2)
            x = i - (int(i/shot_width)*shot_width) + (match_width/2)

            coords = [x,y]
            return coords

    ### match not found
    return False


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1