Wednesday, May 1, 2019

A second image server

Variation on the first.  This server expects the client to send the dimensions of the image to be returned - if the aspect ratio is different to the camera's aspect ratio, the camera image is cropped (centrally) to the correct aspect ratio, resized using thumbnail(), and sent.  Also, rather than sending a byte stream of the image data, I'm using pickle.dumps() to create a byte stream from an Image object, and sending that (since I'm already creating an Image object for the cropping and resizing).



#!/usr/bin/env python3

import argparse
import pickle
import socketserver
import sys
from io import BytesIO
from PIL import Image
from picamera import PiCamera

class CamHandler(socketserver.BaseRequestHandler):
    def handle(self):
        global cam
        global args
        data = self.request.recv(1024)
        width, height = [int(x) for x in data.decode('utf-8').split()]
        if args.debug:
            print("Width {} height {}".format(width, height))
        stream = BytesIO()
        cam.capture(stream, format='jpeg')
        stream.seek(0)
        picture = Image.open(stream)
        ratio1 = width / height
        ratio2 = picture.width / picture.height
        if ratio1 < ratio2:
            # Camera image is broader than desired image ratio
            new_width = ratio1 * picture.height
            delta = (picture.width - new_width) / 2
            picture = picture.crop((delta, 0, (new_width + delta), 
                picture.height))
        if ratio1 > ratio2:
            # Camera image is taller than desired image ratio
            new_height = picture.width / ratio1
            delta = (picture.height - new_height) / 2
            picture = picture.crop((0, delta, picture.width, 
                (new_height + delta)))
        picture.thumbnail((width, height), Image.ANTIALIAS)
        if args.debug:
            print("Sending image {}".format(picture.size))
        self.request.sendall(pickle.dumps(picture))


class ImageServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def __init__ (self, server_address, handler_class=CamHandler):
        socketserver.TCPServer.__init__(self, server_address, handler_class)
        return

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Image server.')
    parser.add_argument('--hostip', '-i', action='store', dest='address', 
        default='127.0.0.1', help='IP address of server host.')
    parser.add_argument('--port', '-p', action='store', type=int, dest='port',
        default=12345, help='Port number.')
    parser.add_argument('--debug', '-d', action='store_true', default=False,
        dest='debug', help='Print debugging information.')
    args = parser.parse_args()

    try:
        cam = PiCamera() 
        cam.rotation = 180
        time.sleep(2)
        if args.debug:
            print("Camera started.")
            print("Server starting on {}:{}".format(args.address, args.port))
        server = ImageServer((args.address, args.port), 
                handler_class=CamHandler)
        server.serve_forever()
    except Exception as e:
        if args.debug:
            print(e)
    except KeyboardInterrupt:
        pass
    finally:
        if args.debug: 
            print("Server shutting down.")


No comments:

Post a Comment