Tuesday, May 30, 2017

Install OpenCV2

Step 1: Install OpenCV software

Follow the following steps mentioned in
https://gist.github.com/dynamicguy/3d1fce8dae65e765f7c4

Here we copy the commands:

# install dependencies
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y cmake
sudo apt-get install -y libgtk2.0-dev
sudo apt-get install -y pkg-config
sudo apt-get install -y python-numpy python-dev
sudo apt-get install -y libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install -y libjpeg-dev libpng-dev libtiff-dev libjasper-dev

sudo apt-get -qq install libopencv-dev build-essential checkinstall cmake pkg-config yasm libjpeg-dev libjasper-dev libavcodec-dev libavformat-dev libswscale-dev libdc1394-22-dev libxine-dev libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev libv4l-dev python-dev python-numpy libtbb-dev libqt4-dev libgtk2.0-dev libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev x264 v4l-utils

# download opencv-2.4.11
wget http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.11/opencv-2.4.11.zip
unzip opencv-2.4.11.zip
cd opencv-2.4.11
mkdir release
cd release

# compile and install
cmake -G "Unix Makefiles" -D CMAKE_CXX_COMPILER=/usr/bin/g++ CMAKE_C_COMPILER=/usr/bin/gcc -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON -D WITH_QT=ON -D WITH_OPENGL=ON -D BUILD_FAT_JAVA_LIB=ON -D INSTALL_TO_MANGLED_PATHS=ON -D INSTALL_CREATE_DISTRIB=ON -D INSTALL_TESTS=ON -D ENABLE_FAST_MATH=ON -D WITH_IMAGEIO=ON -D BUILD_SHARED_LIBS=OFF -D WITH_GSTREAMER=ON ..
make all -j4 # 4 cores
sudo make install


We may get some errors when trying to install some of the libraries in command line. Since if highly depends on the system, we cannot give a general solution here. At the same time, many people have experiences the same problems and it is easy to find the solution through Google.

Step 2: Add Include path and Libraries.

If everything works, now we should have opencv2 installed. The next step is to configure our IDE. In my case, I use the eclipse. The instruction can be found here:
http://docs.opencv.org/2.4/doc/tutorials/introduction/linux_eclipse/linux_eclipse.html



Step 3: Test OpenCV

In this step, we use the first code example in the book Learning OpenCV (https://www.amazon.com/Learning-OpenCV-Computer-Vision-Library/dp/0596516134).

#include "highgui/highgui_c.h" // In the book, we have     
                               // #include "highgui.h"
                               // which is used in the old
                               // version of OpenCV


int main()
{
    IplImage* img = cvLoadImage(  "path-of-an-image-file"  );
    cvNamedWindow(  "Example1", CV_WINDOW_AUTOSIZE  );
    cvShowImage(  "Example1", img  );
    cvWaitKey( 0 );
    cvReleaseImage(  &img  );
    cvDestroyWindow(  "Example1"  );
 }


If we can see a new window of image pop up, we are all good.

Sunday, May 28, 2017

Raspeberry Pi - Connect to Wifi and send ip information to email



There is problem with the current setting of my Raspeberry Pi. Every time when it is started, I need to log into my account and connect to wifi. In order to use ssh on my desktop, I also need the ip information of the Raspberry Pi.

To automate this part, we need to write some autostart program. Essentially, we want to write scripts that can be executed upon the start.

In ArchLinux, it can be doen through systemctl. The documentation can be found here:
https://wiki.archlinux.org/index.php/systemd

It takes a while to read through all the sections. Some Useful examples can be found here:
https://www.freedesktop.org/software/systemd/man/systemd.service.html

Here is what we need to do:

Step 1: Create the script that need to be executed upon the start.

In our case, we want to have something like

#!/bin/sh
ip link set wlan0 down
netctl start home_wifi
ifconfig | mail -v -s "IP information" user@gmail.com


After editing the script, make it executable: chmod + x your-script

Step 2: Create a new service

Go to the /etc/systemd/system folder and create a new file
my-autostart.service

[Unit]
Description=Send ip information through email

[Service]
ExecStart=/path/your-script

[Install]
WantedBy=multi-user.target


Save the edit and enable the service by
systemctl enable my-autostart.service

Caveate
Though it can work, the configuration of the service may not be completely correct. It is better to have more fine control on it.


Tuesday, May 23, 2017

Use Raspberry Pi as a server



With the help of gmail, we can use Raspberry Pi as a server. What we need are:
  • mail: to send/reply email
  • getmail: to get new email messages
It is possible to use plain python to send/receive email, but here it is more convenient to use the existing tools.

The request consists of
  • subject which contains [request@me]. This is the request identifier
  • request message included in the email message. Each request starts with @begin and ends with @end. Each line in between has the form of attributeName = attributeValue. For example, request = take-picture indicates that this request is asking raspberry-pi to take a picture.
 As mentioned in one of the previous post, we need the raspistill working on raspberry pi. Otherwise, the flow is:
  1. Check new email messages by calling getmail -n
  2. Parse the new mail messages if there is any and get the request object
  3. Process the request
Here is the code:


import re
import subprocess
import os
from os import path
import hashlib
from datetime import datetime
import time
import subprocess
import shlex
import logging
import glob


_currDir = os.path.dirname( os.path.abspath( __file__ ) )

# setup logger
logging.basicConfig(level    = logging.DEBUG,
                    format   = '%(asctime)s %(name)s %(levelname)s %(message)s',
                    datefmt  = '%m-%d %H:%M',
                    filename = path.join(_currDir, "_send-emil.log"),
                    filemode ='w')

console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)

logging.info( "current directory: {}".format( _currDir ) )

# settings
REQUEST_IDENTIFIER = '[request@me]'
IDLE               = 'the parser is idle'
SUBJECT            = 'hitted subject'
BEGIN              = 'beginning-of-request'
IMG_FORMAT         = 'jpg'
RQ_TAKE_PICTURE    = 'take-picture'
ARG_NUM            = 'number'
MAIL_DIR           = '/home/meretciel/mail/new'
IMG_DIR            = '/home/meretciel/workspace/camera'
RECEIVERS          = [ 'meretciel.fr@gmail.com' ]


class EmailMessage( object ):
    _COMMAND_TEMPLATE_PLAIN      = r'mail -v -s "{subject}" {receiver}'
    _COMMAND_TEMPLATE_ATTACHMENT = r'mail -v -s "{subject}" {attachment} {receiver}'

    def __init__( self, subject = None, message = None, receiver = None, attachment = None ):
        self._subject    = subject
        self._message    = message
        self._receiver   = receiver
        self._attachment = attachment

    def _generateEmailCommand( self ):
        if isinstance( self._receiver, list ):
            receiver = ';'.join( self._receiver )
        else:
            receiver = self._receiver

        if not self._attachment:
            return EmailMessage._COMMAND_TEMPLATE_PLAIN.format( message = self._message, subject = self._subject, receiver = receiver )
            

        if isinstance( self._attachment, list):
            attachment = ' -a '.join( self._attachment )
            attachment = ' -a ' + attachment

        else:
            attachment = ' -a ' + self._attachment
        

        return EmailMessage._COMMAND_TEMPLATE_ATTACHMENT.format( message = self._message, subject = self._subject, receiver = receiver, attachment = attachment )

    
    def send( self ):
        ''' send email using mail command. We can also implement this in plain python '''
        p1 = subprocess.Popen( 
            shlex.split( r'echo {message}'.format( message = self._message ) ),
            stdout = subprocess.PIPE,
        )
        p2 = subprocess.Popen(
            shlex.split( self._generateEmailCommand() ),
            stdin = subprocess.PIPE
        )
        p2.communicate()

def constructCommand( s_command ):
    l = [ x for x in s_command.split(' ') if x != '' ]
    return l 

def executeCommand( s_command ):
    logging.info( "execute command: {}".format( s_command ) )
    subprocess.call( constructCommand( s_command ) )

def getEmail():
    executeCommand( "getmail -n" )




def checkNewEmail():
    ''' check if there is new ( unread ) emails. '''
    ls_output = subprocess.check_output( constructCommand( "ls -lt {}".format( MAIL_DIR ) ) )
    ls_output = str( ls_output, "utf-8" )
    logging.debug( "ls_output: {}".format( ls_output ) )
    ls_output = ls_output.split('\n')
    ls_output = ls_output[1:]   # the first line is total output
    newFiles  = [ x.split(' ')[-1] for x in ls_output ][::-1]
    newFiles  = [ path.join( MAIL_DIR, x ) for x in newFiles if x != '' ]

    return newFiles



def takePicture( number ):
    ''' ask raspiberry pi to take picture. '''
    time            = datetime.utcnow()
    baseFileName    = path.join( IMG_DIR, hashlib.sha224( str( time ).encode( "utf-8" ) ).hexdigest() )
    commandTemplate = r'/opt/vc/bin/raspistill -n -vf -w 640 -h 480 -e {IMG_FORMAT} {_} -o {baseFileName}%04d.{IMG_FORMAT}'.format( IMG_FORMAT = IMG_FORMAT, baseFileName = baseFileName, _ = "{time}")
    number          = min( 10, int( number ) )
    number          = max( number, 1 ) 

    if number == 1:
        command = commandTemplate.format( time = '' )
        imgFileNames = [ "{baseFileName}-001.{IMG_FORMAT}".format( baseFileName = baseFileName, IMG_FORMAT = IMG_FORMAT ) ]
    else:
        _tl           = 2000    # in milliseconds
        _totalTime    = _tl * ( number - 1)
        _time         = "-t {} -tl {}".format( _totalTime, _tl )
        command       = commandTemplate.format( time = _time )

        imgFileNames = ["{baseFileName}{num}.{IMG_FORMAT}".format( baseFileName = baseFileName, num=str(i).zfill(4), IMG_FORMAT=IMG_FORMAT ) for i in range(number)]

    try:
        executeCommand( command )
        return imgFileNames
    except Exception as e:
       logging.error("Error when taking picture") 
       return []


class Request( object ):

    def __init__( self ):
        self._requestName = None
        self._args = {}

    def load( self, attrName, value ):
        if attrName == 'request':
            self._requestName = value

        else:
            self._args.update( { attrName : value } )

    def process( self ):
        if self._requestName:
            logging.info( 'processing {}'.format( self ) )

            if self._requestName.lower() == RQ_TAKE_PICTURE:
                number = self._args.get( ARG_NUM, 1 )
                imgFiles = takePicture( number )
                logging.debug( '<2> image files : {}'.format( imgFiles ) )
                newEmail = EmailMessage( subject = 'New Images', message = '', receiver = RECEIVERS, attachment = imgFiles )
                newEmail.send()
                for fn in imgFiles:
                    logging.info( "removing the file: {}".format( fn ) )
                    os.remove( fn )


    def __repr__( self ):
        return "Request( name={}, args={} )".format( self._requestName, str( self._args ) )

def _parseEmail( f, state, requests ):
    if state == IDLE:
        line = f.readline()
        while line:
            if 'Subject' in line and REQUEST_IDENTIFIER in line:
                state = SUBJECT
                break
            line = f.readline()
        return line, f, state, requests

    if state == SUBJECT:
        line = f.readline()
        while line:
            if '@begin' in line:
                state = BEGIN
                break
            line = f.readline()
        return line, f, state, requests

    if state == BEGIN:
        pattern = r'(?P<attrName>\w+)\s*=\s*(?P<value>.+)'
        line = f.readline()
        request = Request()
        while line:
            if '@end' in line:
                requests.append( request )
                state = IDLE
                break

            res = re.search( pattern, line )
            if res:
                attrName  = res.group( 'attrName' )
                value     = res.group( 'value' )
                request.load( res.group( 'attrName' ), res.group( 'value' ) )

            line = f.readline()

        return line, f, state, requests
        



def parseEmail( msgFile ):
    logging.info("parsing email file {}".format( msgFile ) )
    with open( msgFile ) as f:
        state = IDLE
        line = '__start__'
        requests = []
        while line:
            logging.info( "processing line : {}".format( line ) )
            line, f, state, request = _parseEmail( f, state, requests )

        return requests

def removeNewMsgFiles( newMsgFiles ):

    for item in newMsgFiles:
        os.remove( item )


def getNewRequestFromEmail():
    getEmail()
    newMsgFiles = checkNewEmail()
    logging.debug(" <1> New messages : {}".format( str( newMsgFiles ) ) ) 
    requests = []
    for newMsgFile in newMsgFiles:
        requests.extend( parseEmail( newMsgFile ) )
    removeNewMsgFiles( newMsgFiles )
    return requests


if __name__ == '__main__':
    
    existingFiles = glob.glob( path.join( MAIL_DIR, r'*.alarmpi' ) )
    for f in existingFiles:
        os.remove( f )

    while True:
        logging.info( "waiting for request." )
        requests = getNewRequestFromEmail()
        for request in requests:
            request.process()

        time.sleep( 10. )




--END--