Skip to content

Instantly share code, notes, and snippets.

@jldiaz
Last active November 6, 2019 13:04
Show Gist options
  • Save jldiaz/e97b16acc295a2b4c5f2af993ddbb232 to your computer and use it in GitHub Desktop.
Save jldiaz/e97b16acc295a2b4c5f2af993ddbb232 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
import numpy
from PIL import ImageGrab, Image
def find_coeffs(pa, pb):
"""Computes the appropiate parameters for PIL.Image.transform
from the coordinates of two rectangles given as parameters:
pa: list of four corners of rectangle after transform
pb: list of four corners of rectangle before transform
"""
# Adapted from (https://stackoverflow.com/questions/14177744/how-does-perspective-transformation-work-in-pil)
A = []
for a, b in zip(pa, pb): # after, before
A.append([a[0], a[1], 1, 0, 0, 0, -b[0] * a[0], -b[0] * a[1]])
A.append([0, 0, 0, a[0], a[1], 1, -b[1] * a[0], -b[1] * a[1]])
A = numpy.array(A)
B = numpy.array(pb).reshape(8)
res = numpy.linalg.solve(A, B)
return res.reshape(8)
def crop_and_undistort(img, screen_corners=None):
"""Extracts and returns the "slide" part of the image
Parameters:
img: input image (frame of video)
screen_corners: coordinates of NW, NE, SE, SW corners of the
screen (slide), for an input image which is 1024px wide
"""
# Get the actual size of img, to adjust scale
w, _ = img.size
if not screen_corners:
# When no screen corners are given, the image is simply
# rescaled to a width of 1024, so that the user can display
# this image and find the coordinates of the corners
img.thumbnail((1024, 1024))
return img
# Otherwise, adjust the screen corners received to the scale of the image
scale = w / 1024
screen_corners = [(int(x * scale), int(y * scale)) for x, y in screen_corners]
# Find the approximate aspect ratio of the slide, and use it to
# compute the height of the output image (the widht will be the
# same than the input image)
approx_aspect_ratio = (screen_corners[1][0] - screen_corners[0][0]) / (
screen_corners[3][1] - screen_corners[0][1]
)
new_h = int(w / approx_aspect_ratio)
# Find the required coefficients for the perspective transform
coeffs = find_coeffs([(0, 0), (w, 0), (w, new_h), (0, new_h)], screen_corners)
# Perform the perspective transform
result = img.transform((w, new_h), Image.PERSPECTIVE, coeffs, Image.BICUBIC)
return result
# For convenience, some screen corners are already found by
# the author, for some example videos
conferences = {
"none": None,
# https://www.youtube.com/watch?v=ySR_FVNX4bQ
"kubecon": [(0, 0), (702, 48), (702, 418), (0, 447)],
# https://www.youtube.com/watch?v=QpaapVaL8Fw
"pydata2015w": [(283, 88), (1013, 73), (1013, 500), (283, 485)],
}
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
conference = sys.argv[1]
else:
conference = "none"
img = ImageGrab.grabclipboard()
if img is None:
print("You should have an image in the clipboard")
quit()
if conference.lower() not in conferences:
print(
"Sorry, '{}' conference is unknown. Omit the conferece parameter".format(
conference
)
)
print("and use the output image as base to measure the coordinates")
print("of the screen, and add them to `conferences` variable in the script")
quit()
screen_coordinates = conferences[conference.lower()]
result = crop_and_undistort(img, screen_coordinates)
result.save("slide.png")
result.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment