Skip to content
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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions ImageServer.py
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some unused imports here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in latest code

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
Copy link
Contributor

Choose a reason for hiding this comment

The 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):
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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]
Copy link

Choose a reason for hiding this comment

The 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)
Copy link

Choose a reason for hiding this comment

The 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?


Copy link
Contributor

Choose a reason for hiding this comment

The 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()
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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.


65 changes: 65 additions & 0 deletions URLtoPebblePng.py
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')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a tempfile module for this kind of thing.

Copy link
Author

Choose a reason for hiding this comment

The 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")
Copy link
Contributor

Choose a reason for hiding this comment

The 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 my-image.png becomes my-image.pebble.png). When used in the web server you should not write the images to disk.

os.remove("TempImage.jpg")

def main():
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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?

Copy link

Choose a reason for hiding this comment

The 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 curl http://the-url | bash, not bash --url http://the-url.

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()
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Python libraries that need to be installed:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a traditional requirements.txt file - it'll choke if someone actually tries pip install -r requirements.txt. Better to put it in the README if the extra pip parameters really are required.

Copy link
Contributor

Choose a reason for hiding this comment

The 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


22 changes: 21 additions & 1 deletion src/js/pebble-js-app.js
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");
});
Expand Down Expand Up @@ -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");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

application/x-www-form-urlencoded probably - though you don't escape anything so it'll probably break if the image URL itself has query parameters.

req1.setRequestHeader("Content-length", imageURL.length);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter isn't strictly required AFAIK.

req1.setRequestHeader("Connection", "close");
Copy link

Choose a reason for hiding this comment

The 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);
Copy link

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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";
Copy link
Contributor

Choose a reason for hiding this comment

The 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");
Expand Down