-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server side conversion scripts #15
base: master
Are you sure you want to change the base?
Changes from all commits
eaa1cb7
9263f72
72bee77
3ead5b8
ce75f06
a2e2718
d847759
1165e2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from URLtoPebblePng import get_pebble_png | ||
import SimpleHTTPServer | ||
import SocketServer | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/simeple/simple/ |
||
# request handler | ||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) | ||
|
||
def do_POST(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. POST is not the right type of request for this. We are not changing the state on the server so we should be using a GET. |
||
|
||
# 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"]) | ||
print ImageURL | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please fix the indentation in this function. Lots of stray spaces in multiple places. Never use print in your code. Use the logging framework and an appropriate log level. |
||
|
||
# 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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...which "excess characters"? Where are they coming from? |
||
PebType = int(PebbleType[3:-2]) | ||
|
||
#performing the conversion for that image | ||
get_pebble_png(ImageURL,PebType) | ||
#sending an OK response to the caller | ||
self.send_response(200) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not return the image immediately, instead of forcing the client to perform a redundant request? |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why 4 blank lines? |
||
|
||
|
||
|
||
Handler = ServerHandler | ||
|
||
httpd = SocketServer.TCPServer(("", PORT), Handler) | ||
|
||
# port that the server is running on | ||
print "Serving at port 8000 forever!" | ||
httpd.serve_forever() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Show an example of how to use it please. |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, show how to use it. |
||
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. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
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): | ||
img = urllib2.urlopen(input_url) | ||
localFile = open('TempImage.jpg', 'wb') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tempfile module didn't work for an image file. It couldn't find the size parameters for the temporary file. But I updated the code to delete the TempImage after it's completed conversion |
||
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: | ||
# 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 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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very very wrong. The output file name should be required on the commande line, or automatically generated from the input file name (for example |
||
os.remove("TempImage.jpg") | ||
|
||
def main(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is great that this script can take a URL as a parameter but it would be even better if it worked with local files too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I add another parameter for choosing whether it's a local file or a URL? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably better to not deal with web requests at all - I can't think of any standard CLI utility that does this. E.g. if you want to execute a bash script from the web, you go |
||
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, args.PebbleType) | ||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Python libraries that need to be installed: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't a traditional There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take a look at the requirements.txt file in the SDK for the proper syntax. |
||
PIL: | ||
pip install PIL --allow-unverified PIL --allow-all-external | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
var transferInProgress = false; | ||
|
||
/*Global URL var for image conversion server | ||
Replace with your server */ | ||
var toServer = "http://35418b8a.ngrok.com"; | ||
|
||
Pebble.addEventListener("ready", function(e) { | ||
console.log("NetDownload JS Ready"); | ||
}); | ||
|
@@ -27,8 +31,24 @@ Pebble.addEventListener("appmessage", function(e) { | |
}); | ||
|
||
function downloadBinaryResource(imageURL, callback, errorCallback) { | ||
|
||
|
||
/*POSTing the image URL to the server for conversion. | ||
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, false); | ||
imageURL = "imageurl=" + imageURL+"&PebbleType=0"; | ||
req1.setRequestHeader("Content-type", "imageURL"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
req1.setRequestHeader("Content-length", imageURL.length); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This parameter isn't strictly required AFAIK. |
||
req1.setRequestHeader("Connection", "close"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here - I've never had to include this - is it something with SimpleHTTPServer? |
||
req1.send(imageURL); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This request happens async, so you're gambling that the file is converted before the next request goes through. With a single-threaded server it's a safe assumption, but not in general. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I converted the POST request to a synchronous request (changed third parameter of req1.open to false). So it waits for the post to complete and receive a response before progressing. |
||
|
||
//getting converted image, which is always at <server>/Pebble_image.png | ||
var req = new XMLHttpRequest(); | ||
req.open("GET", imageURL,true); | ||
var img_return = toServer+ "/Pebble_image.png"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think will happen if two clients try to convert an image at roughly the same time? This is very broken. You need to pass the URL of the image you want to the GET request AND return the converted image directly. |
||
req.open("GET", img_return, true); //do get from image url which is standard | ||
req.responseType = "arraybuffer"; | ||
req.onload = function(e) { | ||
console.log("loaded"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some unused imports here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in latest code