diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..140f8cf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/Ambient.py b/Ambient.py index a0c36a3..b3de225 100644 --- a/Ambient.py +++ b/Ambient.py @@ -2,21 +2,47 @@ from PIL import ImageGrab #pip3 install Pillow from openrgb import OpenRGBClient #pip3 install openrgb-python from openrgb.utils import RGBColor - +from sklearn.cluster import KMeans +from collections import Counter +import numpy as np + #////////////////////////////////////////////////////////////////////////////////////////////////////////// # GLOBAL DEFINES #////////////////////////////////////////////////////////////////////////////////////////////////////////// #HEIGHT = 1920 #now using image.size[1] dynamically #WIDTH = 1200 #now using image.size[0] dynamically LOOP_INTERVAL = 0.05 # how often we calculate screen colour (in seconds) -DURATION = 3 # how long it takes bulb to switch colours (in seconds) -DECIMATE = 10 # skip every DECIMATE number of pixels to speed up calculation +DURATION = 3 # how long it takes bulb to switch colours (in seconds) +DECIMATE = 100 # sample only 1% of pixels to speed up +K_MEANS = 3 # number of clusters to calculate (returns average color if 1 and dominant color if >1) +XSCREENCAPTURE = True # Set to true if on X11, else false +X_RES = 2560 # Screen pixel in x direction +Y_RES = 1440 # screen pixel in y direction client = OpenRGBClient() #will only work if you use default ip/port for OpenRGB server. This is an easy fix, read the documentation if need be https://openrgb-python.readthedocs.io/en/latest/ Dlist = client.devices - + +if XSCREENCAPTURE: + # Use X11 display manager for screen capture + import ctypes + from PIL import Image + LibName = 'prtscn.so' # has to be compiled previously from prtscrn.c + AbsLibPath = os.getcwd() + os.path.sep + LibName # assuming prtscrn.so lives in the same dir + grab = ctypes.CDLL(AbsLibPath) + + def grab_screen(x1,y1,x2,y2): + w, h = x2-x1, y2-y1 + size = w * h + objlength = size * 3 + + grab.getScreen.argtypes = [] + result = (ctypes.c_ubyte*objlength)() + + grab.getScreen(x1,y1, w, h, result) + return Image.frombuffer('RGB', (w, h), result, 'raw', 'RGB', 0, 1) + def SetStatic(): for Device in Dlist: time.sleep(0.1) @@ -36,6 +62,13 @@ def hex_to_rgb(value): lv = len(value) return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3)) +# init K-Means model +clt = KMeans(n_clusters = 3, n_init=3, max_iter=200) + +# init vars for colors before current +old2 = np.zeros(3) +old = np.zeros(3) + # run loop while True: #init counters/accumulators @@ -48,21 +81,33 @@ def hex_to_rgb(value): #////////////////////////////////////////////////////////////////////////////////////////////////////////// # CALCULATE AVERAGE SCREEN COLOUR #////////////////////////////////////////////////////////////////////////////////////////////////////////// - image = ImageGrab.grab() # take a screenshot + if XSCREENCAPTURE: + image = grab_screen(0,0,X_RES,Y_RES) # take a screenshot usign X + else: + image = ImageGrab.grab() # take a screenshot using PIL #print image.size - + data = [] for y in range(0, image.size[1], DECIMATE): #loop over the height for x in range(0, image.size[0], DECIMATE): #loop over the width color = image.getpixel((x, y)) #grab a pixel - red += color[0] - green += color[1] - blue += color[2] + data.append(color) - - red = (( red / ( (image.size[1]/DECIMATE) * (image.size[0]/DECIMATE) ) ) ) - green = ((green / ( (image.size[1]/DECIMATE) * (image.size[0]/DECIMATE) ) ) ) - blue = ((blue / ( (image.size[1]/DECIMATE) * (image.size[0]/DECIMATE) ) ) ) + #cluster and assign labels to the pixels + labels = clt.fit_predict(data) + #count labels to find most popular + label_counts = Counter(labels) + #subset out most popular centroid + dominant_color = clt.cluster_centers_[label_counts.most_common()[0][0]] + + # current color is blended with colors 2 frames before to smooth effect + dominant_color += (old + 0.5*old2) + dominant_color /= 2.5 + # reset vars for next iteration + old = dominant_color + old2 = old + + red, green, blue = dominant_color #print(red, green, blue) for Device in Dlist: Device.set_color(RGBColor(int(red), int(green), int(blue))) diff --git a/prtscn.c b/prtscn.c new file mode 100644 index 0000000..7c05f08 --- /dev/null +++ b/prtscn.c @@ -0,0 +1,36 @@ +#include +#include +#include +//Compile hint: gcc -shared -O3 -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c -lX11 +//Credits: https://stackoverflow.com/questions/69645/take-a-screenshot-via-a-python-script-on-linux + +void getScreen(const int, const int, const int, const int, unsigned char *); +void getScreen(const int xx,const int yy,const int W, const int H, /*out*/ unsigned char * data) +{ + Display *display = XOpenDisplay(NULL); + Window root = DefaultRootWindow(display); + + XImage *image = XGetImage(display,root, xx,yy, W,H, AllPlanes, ZPixmap); + + unsigned long red_mask = image->red_mask; + unsigned long green_mask = image->green_mask; + unsigned long blue_mask = image->blue_mask; + int x, y; + int ii = 0; + for (y = 0; y < H; y++) { + for (x = 0; x < W; x++) { + unsigned long pixel = XGetPixel(image,x,y); + unsigned char blue = (pixel & blue_mask); + unsigned char green = (pixel & green_mask) >> 8; + unsigned char red = (pixel & red_mask) >> 16; + + data[ii + 2] = blue; + data[ii + 1] = green; + data[ii + 0] = red; + ii += 3; + } + } + XDestroyImage(image); + XDestroyWindow(display, root); + XCloseDisplay(display); +}