Procházet zdrojové kódy

Added seychelles command

Kirk Trombley před 6 roky
rodič
revize
f2aa91d6ef
4 změnil soubory, kde provedl 217 přidání a 2 odebrání
  1. 0 1
      TODO_commands
  2. 1 0
      config/config.toml
  3. 3 1
      requirements.txt
  4. 213 0
      src/plugins/seychelles.py

+ 0 - 1
TODO_commands

@@ -1,5 +1,4 @@
 old:
-seychelles
 pokemon
 say
 digest

+ 1 - 0
config/config.toml

@@ -6,6 +6,7 @@ teamspeak = [ "teamspeak" ]
 session = [ "session" ]
 roll = [ "roll" ]
 querying = [ "inspire", "shield", "scp", "riddle" ]
+seychelles = [ "seychelles" ]
 
 [teamspeak]
 host = "kirkleon.ddns.net"

+ 3 - 1
requirements.txt

@@ -4,4 +4,6 @@ Flask
 gunicorn
 requests
 dice
-requests_html
+requests_html
+pillow
+

+ 213 - 0
src/plugins/seychelles.py

@@ -0,0 +1,213 @@
+from urllib.request import urlretrieve
+import math
+import os
+
+from PIL import Image
+
+from command_system import RollbotResponse, RollbotFailure, as_plugin
+from util import upload_image
+from config import API_KEY
+
+
+@as_plugin
+def seychelles(db, message):
+    argstr = message.raw_args
+
+    if argstr is None or "i.imgur.com" not in argstr:
+        return RollbotResponse(
+            message,
+            failure=RollbotFailure.INVALID_ARGUMENTS,
+            debugging={
+                "explain": "The !seychelles command requires a direct imgur link argument."
+            }
+        )
+
+    try:
+        urlretrieve(argstr, "/tmp/seychelles")
+    except Exception as e:
+        return RollbotResponse(
+            message,
+            failure=RollbotFailure.SERVICE_DOWN,
+            debugging={
+                "explain": "Couldn't download the provided image.",
+                "exception": e
+            }
+        )
+
+    s = Seychelles("/tmp/seychelles", name_out="/tmp/seychelles_out", ext_out="png")
+    s.seychelles(verbose=True)
+    s.save()
+
+    with open("/tmp/seychelles_out.png", "rb") as s_out:
+        success, result = upload_image(API_KEY, s_out)
+
+    if success:
+        return RollbotResponse(message, img=result)
+    else:
+        return RollbotResponse(
+            message,
+            failure=RollbotFailure.SERVICE_DOWN,
+            debugging=result
+        )
+
+
+# By Akshay Chitale for r/vexillology on Reddit
+
+# For Python 3
+
+class Seychelles:
+    def __init__(self, name_in, size_out=None, name_out=None, ext_out=None):
+        # Set up input
+        self.name_in, self.ext_in = os.path.splitext(name_in)
+        self.img_raw = Image.open(name_in)
+        self.img_raw = self.img_raw.convert('RGB')
+        self.size_in = self.img_raw.size
+        # Flip so that colors are measured from top left
+        self.img_in = self.img_raw.transpose(Image.FLIP_TOP_BOTTOM)
+
+        # Set up output
+        self.size_out = size_out if size_out else self.size_in
+        self.name_out = name_out if name_out else self.name_in + '_out'
+        self.ext_out = '.' + ext_out if ext_out else self.ext_in
+        self.img_out = Image.new('RGB', self.size_out)
+        self.pixels_out = self.img_out.load()
+
+        # Set up image to print
+        self.img_print = None
+
+    def _angle_transfer(self, diagonal, seychelles):
+        # Define transfer curve as a parabola
+        x1, y1 = 0, 0
+        x2, y2 = (diagonal, math.pi/4)
+        x3, y3 = math.pi/2, math.pi/2
+        # Below code from http://chris35wills.github.io/parabola_python/
+        denom = (x1-x2) * (x1-x3) * (x2-x3);
+        A     = (x3 * (y2-y1) + x2 * (y1-y3) + x1 * (y3-y2)) / denom;
+        B     = (x3*x3 * (y1-y2) + x2*x2 * (y3-y1) + x1*x1 * (y2-y3)) / denom;
+        # C = 0 guaranteed because parabola goes through (0,0)
+        if seychelles:
+            # Going forward, use parabola
+            return (lambda x: A*x*x + B*x)
+        else:
+            # Inverse, solve parabola y=ax^2+bx
+            # Watch out for squares, where diagonal is pi/4 so A=0
+            if abs(diagonal - math.pi/4) < 1E-9:
+                return (lambda x: x)
+            else:
+                return (lambda x: (-1*B + math.sqrt(B*B - 4*A*(-x)))/(2*A))
+
+    def seychelles(self, verbose=False):
+        # Diagonal angle of output image
+        out_diagonal = math.atan2(self.size_out[1], self.size_out[0])
+        angle_transfer = self._angle_transfer(out_diagonal, True)
+        # Find output color for each output pixel
+        if verbose:
+            print(' Progress:   0%', end='\r')
+        for x in range(self.size_out[0]):
+            if verbose:
+                print('\r Progress: ' + str(int(100*x/self.size_out[0])).rjust(3) + '%', end='\r')
+            for y in range(self.size_out[1]):
+                # First, get the angle
+                out_angle = math.atan2(y, x)
+
+                # Then, follow the vector to the end of the flag
+                out_x, out_y = x, y
+                if x == 0 and y == 0:
+                    out_x, out_y = 1.0, 1.0
+                elif out_angle < out_diagonal:
+                    # Scale by x
+                    out_x *= self.size_out[0]*1.0/x
+                    out_y *= self.size_out[0]*1.0/x
+                else:
+                    # Scale by y
+                    out_x *= self.size_out[1]*1.0/y
+                    out_y *= self.size_out[1]*1.0/y
+
+                # Get ratio of point radius to full radius
+                point_rad = math.sqrt(x*x + y*y)
+                out_rad = math.sqrt(out_x*out_x + out_y*out_y)
+                rad_ratio = point_rad / out_rad
+
+                # Coordinates on the input are:
+                # x = radius, scaled by width of input
+                in_x = rad_ratio * self.size_in[0]
+                # y = angle, scaled by height of input
+                in_y = angle_transfer(out_angle) * self.size_in[1] * 2.0 / math.pi
+
+                # Ensure coordinates are within range
+                in_x_int = int(round(in_x))
+                if in_x_int < 0:
+                    in_x_int = 0
+                elif in_x_int >= self.size_in[0]:
+                    in_x_int = self.size_in[0] - 1
+                in_y_int = int(round(in_y))
+                if in_y_int < 0:
+                    in_y_int = 0
+                elif in_y_int >= self.size_in[1]:
+                    in_y_int = self.size_in[1] - 1
+
+                # Assign input color to output color
+                self.pixels_out[x,y] = self.img_in.getpixel((in_x_int, in_y_int))
+        if verbose:
+                print('\r Progress: 100%')
+        # Flip so that seychelles is from bottom left
+        self.img_print = self.img_out.transpose(Image.FLIP_TOP_BOTTOM)
+
+
+    def inverse_seychelles(self, verbose=False):
+        # Diagonal angle of input image
+        in_diagonal = math.atan2(self.size_in[1], self.size_in[0])
+        angle_transfer = self._angle_transfer(in_diagonal, False)
+        # Find output color for each output pixel
+        if verbose:
+            print(' Progress:   0%', end='\r')
+        for x in range(self.size_out[0]):
+            if verbose:
+                print('\r Progress: ' + str(int(100*x/self.size_out[0])).rjust(3) + '%', end='\r')
+            for y in range(self.size_out[1]):
+                # First, get the angle
+                in_angle = angle_transfer(y * math.pi / 2.0 / self.size_out[1])
+
+                # Get ratio of point to full width
+                rad_ratio = x * 1.0 / self.size_out[0]
+
+                # Then, follow the vector to the end of the flag
+                if in_angle < in_diagonal:
+                    # Find by x
+                    in_x = self.size_in[0] * 1.0
+                    in_y = in_x * math.tan(in_angle)
+                else:
+                    # Find by y
+                    in_y = self.size_in[1] * 1.0
+                    in_x = in_y / math.tan(in_angle)
+                # Scale by radius ratio
+                in_x, in_y = rad_ratio*in_x, rad_ratio*in_y
+
+                # Ensure coordinates are within range
+                in_x_int = int(round(in_x))
+                if in_x_int < 0:
+                    in_x_int = 0
+                elif in_x_int >= self.size_in[0]:
+                    in_x_int = self.size_in[0] - 1
+                in_y_int = int(round(in_y))
+                if in_y_int < 0:
+                    in_y_int = 0
+                elif in_y_int >= self.size_in[1]:
+                    in_y_int = self.size_in[1] - 1
+
+                # Assign input color to output color
+                self.pixels_out[x,y] = self.img_in.getpixel((in_x_int, in_y_int))
+        if verbose:
+                print('\r Progress: 100%')
+        # Flip so that seychelles is from bottom left
+        self.img_print = self.img_out.transpose(Image.FLIP_TOP_BOTTOM)
+
+    def save(self, name_out=None, ext_out=None):
+        if self.img_print is None: raise Exception('No processing done yet')
+        name = name_out if name_out else self.name_out
+        ext = '.' + ext_out if ext_out else self.ext_out
+        self.img_print.save(name + ext)
+
+    def show(self):
+        if self.img_print is None: raise Exception('No processing done yet')
+        self.img_print.show()