From eaa1cb762a99850bd16c70e6ba80bed983a9aa89 Mon Sep 17 00:00:00 2001 From: VARUN REDDY Date: Tue, 12 May 2015 14:43:43 -0700 Subject: [PATCH 1/8] Added conversion server and modified js --- ImageServer.py | 48 +++++++++++++++++++++++++++++++++++ URLtoPebblePng.py | 56 +++++++++++++++++++++++++++++++++++++++++ src/js/pebble-js-app.js | 18 ++++++++++++- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 ImageServer.py create mode 100644 URLtoPebblePng.py diff --git a/ImageServer.py b/ImageServer.py new file mode 100644 index 0000000..9783929 --- /dev/null +++ b/ImageServer.py @@ -0,0 +1,48 @@ +from URLtoPebblePng import get_pebble_png +import SimpleHTTPServer +import SocketServer +import logging +import cgi +import sys +import urlparse + +PORT = 8000 + +class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + + def do_GET(self): + # For a get request, just return the Pebble_image.png + # This is taken care of by the simeple HTTP server + # request handler + return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + + def do_POST(self): + + # Get the length of the image URL from the HTTP headers + length = int(self.headers['Content-length']) + + # Storing data in a post data structure. + # Can send multiple segments of data which can be decoded and + # stored in post_data. + post_data = urlparse.parse_qs(self.rfile.read(length).decode('utf-8')) + + # Image url is stored at the imageurl key + ImageURL = str(post_data["imageurl"]) + + # Using substring to remove excess characters + ImageURL = ImageURL[3:-2] + #performing the conversion for that image + get_pebble_png(ImageURL) + #sending an OK response to the caller + self.send_response(200) + + + + +Handler = ServerHandler + +httpd = SocketServer.TCPServer(("", PORT), Handler) + +# port that the server is running on +print "Serving at port 8000 forever!" +httpd.serve_forever() \ No newline at end of file diff --git a/URLtoPebblePng.py b/URLtoPebblePng.py new file mode 100644 index 0000000..a0d4e22 --- /dev/null +++ b/URLtoPebblePng.py @@ -0,0 +1,56 @@ +import png +import feedparser +import itertools +import Image +import urllib2 +import ctypes +from HTMLParser import HTMLParser +from PIL import ImageEnhance +import argparse + +#Get image from URL and generate pebble compliant png +def get_pebble_png(input_url): + img = urllib2.urlopen(input_url) + localFile = open('TempImage.jpg', 'wb') + localFile.write(img.read()) + localFile.close() + + #Getting current aspect ratio of image + tempIm = Image.open('TempImage.jpg') + #first index is the width + width = tempIm.size[0] + #second indes is the height + height = tempIm.size[1] + + # Enhancing sharpness of image by a factor of 2. + # Not essential + enhancer = ImageEnhance.Sharpness(tempIm) + enhancer.enhance(2); + + #maintaining aspect ratio of image + ratio = min(144/float(width),168/float(height)) + size = int(float(width*ratio)),int(float(height*ratio)) + + + #resizing image to fit Pebble screen + im_smaller = tempIm.resize(size,Image.ANTIALIAS) + + #converting to 64 color Pebble scheme and dithering image + dithered_im = im_smaller.convert(mode='P', + colors=64, + dither=1, + palette=Image.FLOYDSTEINBERG) + + #saving dithered image as PNG + dithered_im.save("Pebble_image.png","PNG") + + +def main(): + parser = argparse.ArgumentParser( + description='Get image from URL and convert to 64-color palettized PNG for Pebble Time') + parser.add_argument('input_url', type=str, help='URL from which to convert image') + args = parser.parse_args() + get_pebble_png(args.input_url, args.output_filename) + +if __name__ == '__main__': + main() diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index d906892..220f9b0 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -1,5 +1,8 @@ var transferInProgress = false; +//Global URL var for image conversion server +var toServer = "http://35418b8a.ngrok.com"; + Pebble.addEventListener("ready", function(e) { console.log("NetDownload JS Ready"); }); @@ -27,8 +30,21 @@ Pebble.addEventListener("appmessage", function(e) { }); function downloadBinaryResource(imageURL, callback, errorCallback) { + + + //POSTing the image URL to the server for conversion + var req1 = new XMLHttpRequest(); + req1.open("POST",toServer, true); + imageURL = "imageurl=" + imageURL; + req1.setRequestHeader("Content-type", "imageURL"); + req1.setRequestHeader("Content-length", imageURL.length); + req1.setRequestHeader("Connection", "close"); + req1.send(imageURL); + + //getting converted image, which is always at /Pebble_image.png var req = new XMLHttpRequest(); - req.open("GET", imageURL,true); + var img_return = toServer+ "/Pebble_image.png"; + req.open("GET", img_return, true); //do get from image url which is standard req.responseType = "arraybuffer"; req.onload = function(e) { console.log("loaded"); From 9263f72f2c7da6058a34d3a46c362ee1a1503bc9 Mon Sep 17 00:00:00 2001 From: VARUN REDDY Date: Tue, 12 May 2015 16:34:11 -0700 Subject: [PATCH 2/8] Added more comments for explanation --- URLtoPebblePng.py | 9 +-------- src/js/pebble-js-app.js | 8 ++++++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/URLtoPebblePng.py b/URLtoPebblePng.py index a0d4e22..19566f5 100644 --- a/URLtoPebblePng.py +++ b/URLtoPebblePng.py @@ -18,27 +18,20 @@ def get_pebble_png(input_url): #Getting current aspect ratio of image tempIm = Image.open('TempImage.jpg') #first index is the width - width = tempIm.size[0] + width = tempIm.size[0] #second indes is the height height = tempIm.size[1] - # Enhancing sharpness of image by a factor of 2. - # Not essential - enhancer = ImageEnhance.Sharpness(tempIm) - enhancer.enhance(2); - #maintaining aspect ratio of image ratio = min(144/float(width),168/float(height)) size = int(float(width*ratio)),int(float(height*ratio)) - #resizing image to fit Pebble screen im_smaller = tempIm.resize(size,Image.ANTIALIAS) #converting to 64 color Pebble scheme and dithering image dithered_im = im_smaller.convert(mode='P', colors=64, - dither=1, palette=Image.FLOYDSTEINBERG) #saving dithered image as PNG diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index 220f9b0..b59d0c3 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -1,6 +1,7 @@ var transferInProgress = false; -//Global URL var for image conversion server +/*Global URL var for image conversion server + Replace with your server */ var toServer = "http://35418b8a.ngrok.com"; Pebble.addEventListener("ready", function(e) { @@ -32,7 +33,10 @@ Pebble.addEventListener("appmessage", function(e) { function downloadBinaryResource(imageURL, callback, errorCallback) { - //POSTing the image URL to the server for conversion + /*POSTing the image URL to the server for conversion. + This conversion is only for Pebble Time. On a original + Pebble the dithering should convert the image to have + to 2 colors*/ var req1 = new XMLHttpRequest(); req1.open("POST",toServer, true); imageURL = "imageurl=" + imageURL; From 72bee7764fdf6482117c77e74894e030a5c81dd1 Mon Sep 17 00:00:00 2001 From: VARUN REDDY Date: Tue, 12 May 2015 16:42:35 -0700 Subject: [PATCH 3/8] Modified the README --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index b40c2c8..41c8c0b 100644 --- a/README.md +++ b/README.md @@ -100,3 +100,17 @@ on image processing, please refer to the [Advanced techniques videos](https://developer.getpebble.com/events/developer-retreat-2014/) from the Pebble Developer Retreat 2014. +## Scripts for server side conversion + +Two python scripts for conversion of images to Pebble Time compliant images: + +- URLtoPebblePng.py makes use of the Python PIL library to read the URL of the + image. It then resizes the image and dithers it to use only 64 colors +- ImageServer.py is used to field conversion requests and serve the converted + images. + +The pebble-js-app.js was modified to make POST requests to the server, followed +by GET requests to get the converted image. The URL of the server fielding the +requests should be changed by the user. + + From 3ead5b84dc4b9ae529c7234af25bfc425fa6f955 Mon Sep 17 00:00:00 2001 From: VARUN REDDY Date: Tue, 12 May 2015 16:49:44 -0700 Subject: [PATCH 4/8] Requirements file for PIL --- requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8839861 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +Python libraries that need to be installed: +PIL: +pip install PIL --allow-unverified PIL --allow-all-external + + From ce75f068a4dee21363ba424904ca3916c632d0ef Mon Sep 17 00:00:00 2001 From: VARUN REDDY Date: Wed, 13 May 2015 11:09:41 -0700 Subject: [PATCH 5/8] Made some changes based on comments --- URLtoPebblePng.py | 10 ++++------ src/js/pebble-js-app.js | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/URLtoPebblePng.py b/URLtoPebblePng.py index 19566f5..2ee6fdb 100644 --- a/URLtoPebblePng.py +++ b/URLtoPebblePng.py @@ -1,12 +1,8 @@ -import png -import feedparser -import itertools import Image import urllib2 import ctypes -from HTMLParser import HTMLParser -from PIL import ImageEnhance import argparse +import os #Get image from URL and generate pebble compliant png def get_pebble_png(input_url): @@ -37,13 +33,15 @@ def get_pebble_png(input_url): #saving dithered image as PNG dithered_im.save("Pebble_image.png","PNG") + os.remove("TempImage.jpg") + def main(): parser = argparse.ArgumentParser( description='Get image from URL and convert to 64-color palettized PNG for Pebble Time') parser.add_argument('input_url', type=str, help='URL from which to convert image') args = parser.parse_args() - get_pebble_png(args.input_url, args.output_filename) + get_pebble_png(args.input_url) if __name__ == '__main__': main() diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index b59d0c3..ca6b4b2 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -34,11 +34,11 @@ function downloadBinaryResource(imageURL, callback, errorCallback) { /*POSTing the image URL to the server for conversion. - This conversion is only for Pebble Time. On a original + This conversion is only for Pebble Time. On an original Pebble the dithering should convert the image to have to 2 colors*/ var req1 = new XMLHttpRequest(); - req1.open("POST",toServer, true); + req1.open("POST",toServer, false); imageURL = "imageurl=" + imageURL; req1.setRequestHeader("Content-type", "imageURL"); req1.setRequestHeader("Content-length", imageURL.length); From a2e271824af7a80b41e789937d308f5a3b8d82c0 Mon Sep 17 00:00:00 2001 From: Varun Reddy Date: Mon, 18 May 2015 11:01:17 -0700 Subject: [PATCH 6/8] Added conversion for OG Pebble --- URLtoPebblePng.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/URLtoPebblePng.py b/URLtoPebblePng.py index 2ee6fdb..6514f48 100644 --- a/URLtoPebblePng.py +++ b/URLtoPebblePng.py @@ -5,7 +5,7 @@ import os #Get image from URL and generate pebble compliant png -def get_pebble_png(input_url): +def get_pebble_png(input_url, PebbleType): img = urllib2.urlopen(input_url) localFile = open('TempImage.jpg', 'wb') localFile.write(img.read()) @@ -25,10 +25,15 @@ def get_pebble_png(input_url): #resizing image to fit Pebble screen im_smaller = tempIm.resize(size,Image.ANTIALIAS) - #converting to 64 color Pebble scheme and dithering image - dithered_im = im_smaller.convert(mode='P', - colors=64, - palette=Image.FLOYDSTEINBERG) + if PebbleType == 1: + #converting to 64 color Pebble scheme and dithering image + dithered_im = im_smaller.convert(mode='P', + colors=64, + palette=Image.FLOYDSTEINBERG) + else: + #converting to graysclae and then dithering to black and white + im_tempo = im_smaller.convert('LA') + dithered_im = im_smaller.convert('1') #saving dithered image as PNG dithered_im.save("Pebble_image.png","PNG") @@ -40,8 +45,9 @@ def main(): parser = argparse.ArgumentParser( description='Get image from URL and convert to 64-color palettized PNG for Pebble Time') parser.add_argument('input_url', type=str, help='URL from which to convert image') + parser.add_argument('PebbleType', type=int, help='0 is OG Pebble, 1 is Pebble Time') args = parser.parse_args() - get_pebble_png(args.input_url) + get_pebble_png(args.input_url, args.PebbleType) if __name__ == '__main__': main() From d8477594fed742bace07b84c2ad9d95a7768a624 Mon Sep 17 00:00:00 2001 From: Varun Reddy Date: Mon, 18 May 2015 12:29:18 -0700 Subject: [PATCH 7/8] Changed server for converting based on Pebble Type --- ImageServer.py | 22 ++++++++++++++-------- src/js/pebble-js-app.js | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ImageServer.py b/ImageServer.py index 9783929..d0d55ec 100644 --- a/ImageServer.py +++ b/ImageServer.py @@ -1,7 +1,6 @@ from URLtoPebblePng import get_pebble_png import SimpleHTTPServer import SocketServer -import logging import cgi import sys import urlparse @@ -13,7 +12,7 @@ class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def do_GET(self): # For a get request, just return the Pebble_image.png # This is taken care of by the simeple HTTP server - # request handler + # request handler return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) def do_POST(self): @@ -25,19 +24,26 @@ def do_POST(self): # Can send multiple segments of data which can be decoded and # stored in post_data. post_data = urlparse.parse_qs(self.rfile.read(length).decode('utf-8')) - + # Image url is stored at the imageurl key ImageURL = str(post_data["imageurl"]) - + print ImageURL + + # Get type of Pebble you want to do the conversion for + PebbleType = str(post_data["PebbleType"]) + print PebbleType + # Using substring to remove excess characters ImageURL = ImageURL[3:-2] + PebType = int(PebbleType[3:-2]) + #performing the conversion for that image - get_pebble_png(ImageURL) + get_pebble_png(ImageURL,PebType) #sending an OK response to the caller self.send_response(200) - - + + Handler = ServerHandler @@ -45,4 +51,4 @@ def do_POST(self): # port that the server is running on print "Serving at port 8000 forever!" -httpd.serve_forever() \ No newline at end of file +httpd.serve_forever() diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index ca6b4b2..528bddb 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -39,7 +39,7 @@ function downloadBinaryResource(imageURL, callback, errorCallback) { to 2 colors*/ var req1 = new XMLHttpRequest(); req1.open("POST",toServer, false); - imageURL = "imageurl=" + imageURL; + imageURL = "imageurl=" + imageURL+"&PebbleType=0"; req1.setRequestHeader("Content-type", "imageURL"); req1.setRequestHeader("Content-length", imageURL.length); req1.setRequestHeader("Connection", "close"); From 1165e2c740a6298c3d62e6f91543505c49ff9c9a Mon Sep 17 00:00:00 2001 From: Varun Reddy Date: Wed, 20 May 2015 11:49:22 -0700 Subject: [PATCH 8/8] Used Pebble palette before dithering --- URLtoPebblePng.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/URLtoPebblePng.py b/URLtoPebblePng.py index 6514f48..5b4cef7 100644 --- a/URLtoPebblePng.py +++ b/URLtoPebblePng.py @@ -1,8 +1,18 @@ -import Image +from PIL import Image import urllib2 import ctypes import argparse import os +import png + +# Create pebble 64 colors-table (r, g, b - 2 bits per channel) +def pebble_get_64color_palette(): + pebble_palette = [] + for i in xrange(0, 64): + pebble_palette.append(((i >> 4) & 0x3) * 85) # R + pebble_palette.append(((i >> 2) & 0x3) * 85) # G + pebble_palette.append(((i ) & 0x3) * 85) # B + return pebble_palette #Get image from URL and generate pebble compliant png def get_pebble_png(input_url, PebbleType): @@ -10,37 +20,39 @@ def get_pebble_png(input_url, PebbleType): localFile = open('TempImage.jpg', 'wb') localFile.write(img.read()) localFile.close() - #Getting current aspect ratio of image tempIm = Image.open('TempImage.jpg') #first index is the width width = tempIm.size[0] #second indes is the height height = tempIm.size[1] - #maintaining aspect ratio of image ratio = min(144/float(width),168/float(height)) size = int(float(width*ratio)),int(float(height*ratio)) - #resizing image to fit Pebble screen im_smaller = tempIm.resize(size,Image.ANTIALIAS) - + Palet = pebble_get_64color_palette() if PebbleType == 1: - #converting to 64 color Pebble scheme and dithering image - dithered_im = im_smaller.convert(mode='P', - colors=64, - palette=Image.FLOYDSTEINBERG) + # Two step conversion process for using Pebble Time palette + # and then dithering image + paletteIm = Image.new('P',size) + paletteIm.putpalette(Palet * 4) + dithered_im = im_smaller.convert(mode='RGB', + dither=1, + palette=paletteIm) + + dithered_im = dithered_im.convert(mode='P', + dither=1, + colors=64, + palette=Image.FLOYDSTEINBERG) else: - #converting to graysclae and then dithering to black and white + #converting to grayscale and then dithering to black and white im_tempo = im_smaller.convert('LA') dithered_im = im_smaller.convert('1') - #saving dithered image as PNG dithered_im.save("Pebble_image.png","PNG") - os.remove("TempImage.jpg") - def main(): parser = argparse.ArgumentParser( description='Get image from URL and convert to 64-color palettized PNG for Pebble Time')