Last active
November 6, 2019 13:04
-
-
Save jldiaz/e97b16acc295a2b4c5f2af993ddbb232 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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