Monday, 10 December 2018

Filter failed error for Kyocera printers in Linux is caused by a BUG in the device driver

I recently got a new Kyocera FS-1025MFP unit and the print jobs would stop if there are Chinese characters in the job title. The OS is Ubuntu 16.04. Specifically, the error message was either "filter failed" or "filter crashed". Upon checking the CUPS error messages, I discovered that this was due to an error signal 6 returned by the "rastertokpsl" program shipped with the Kyocera driver.

To correct this problem:

1. Download, and install the Kyocera driver by running install.sh as root. This will copy the printer's PPD file to /usr/share/cups/model/Kyocera/ and the rastertokpsl program to /usr/lib/cups/filter/.

2. Create a wrapper for rastertokpsl by:
sudo nano /usr/lib/cups/filter/rastertokpsl-fixed
and then insert the following text:
#!/bin/bash
jobname=$(echo $3 | egrep -o '[[:alnum:]]' | tr -d '\n' | tail -c 20)
path=/usr/lib/cups/filter
$path/rastertokpsl "$1" "$2" "$jobname" "$4" "$5"
3. Make sure that the wrapper has execution permission:
sudo chmod a+x /usr/lib/cups/filter/rastertokpsl-fixed
4. Edit the PPD file specific to your printer model as found in /usr/share/cups/model/Kyocera/. For instance, mine is an FS-1025MFP and the file name is Kyocera_FS-1025MFPGDI.ppd. The only modification that should be made is the cupsFilter line (line 55 in my case):
Find:
*cupsFilter: "application/vnd.cups-raster 0 /usr/lib/cups/filter/rastertokpsl"
and replace the line with:
*cupsFilter: "application/vnd.cups-raster 0 /usr/lib/cups/filter/rastertokpsl-fixed"
The above steps should fix the problem. Alternatively, you may edit the PPD file shipped with the installation package and create the rastertokpsl-fixed wrapper for the installation files, and add the wrapper into install.sh so that you can be able to install the corrected driver with a single command.

I think the problem also affects FS-1040, FS-1060DN, FS-1020MFP, FS-1120MFP and FS-1125MFP, since they are using the same rastertokpsl program.

Wednesday, 10 January 2018

No module named wheel error while importing wheel from setuptools, in Tensorflow compilation

While compiling TensorFlow from source, you might want to build a .whl file with the build_pip_package script generated by bazel. If you see an error message like this:
File "/home/twang/anaconda2/envs/tensorflow/lib/python2.7/site-packages/setuptools/package_index.py", line 31, in <module>
     from setuptools.wheel import Wheel
ImportError: No module named wheel
It is most likely your setuptools is not current. In particular, setuptools installed by conda by default has version 36.5, which does not support wheel. To upgrade setuptools to 38.4 which supports wheel, run:
conda install -c conda-forge setuptools

Monday, 27 November 2017

Setting up port forwarding on Ubuntu 16.04

Here we demonstrate how to set up port forwarding on a VPS with Ubuntu 16.04, so that we can use this VPS as an Internet traffic forwarding service. This setup is useful when the route between the source and the destination IPs is bad, but the intermediate VPS has good connections to both the source and the destination.

First, set the net.ipv4.ip_forward=1 flag in the /etc/sysctl.conf file with vi, and use the following command to make it effective immediately:
sysctl -p
Next, say we want to use port 50000 to forward both TCP and UDP traffic to 168.10.0.1:40000. We use the following iptables commands:
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 50000 -j DNAT --to-destination 168.10.0.1:40000
iptables -t nat -A PREROUTING -p udp -m udp --dport 50000 -j DNAT --to-destination 168.10.0.1:40000
iptables -t nat -A POSTROUTING -d 168.10.0.1/32 -p tcp -m tcp --dport 40000 -j SNAT --to-source 172.10.0.1
iptables -t nat -A POSTROUTING -d 168.10.0.1/32 -p udp -m udp --dport 40000 -j SNAT --to-source 172.10.0.1
In the command above, 172.10.0.1 is the private IP of the intermediate VPS. We can use the following command to check if we set up the NAT table correctly:
iptables -L -n -t nat
Finally, to make the iptables settings persistent, install:
apt-get install iptables-persistent
To save any additional changes to the NAT table, run:
netfilter-persistent save

Friday, 14 July 2017

Making predictions from images with models trained with the TensorFlow LeNet tutorial

Suppose we have trained a model with the TensorFlow LeNet tutorial, as outlined in this post. The following codes would allow you to read an image from disk, and make predictions with the trained LeNet model:
import tensorflow as tf
from tensorflow.contrib.layers import flatten
import cv2
import numpy as np

def LeNet(x):
    # Hyperparameters
    mu = 0
    sigma = 0.1

    # SOLUTION: Layer 1: Convolutional. Input = 32x32x1. Output = 28x28x6.
    conv1_W = tf.Variable(tf.truncated_normal(shape=(5, 5, 1, 6), mean=mu, stddev=sigma))
    conv1_b = tf.Variable(tf.zeros(6))
    conv1 = tf.nn.conv2d(x, conv1_W, strides=[1, 1, 1, 1], padding='VALID') + conv1_b

    # SOLUTION: Activation.
    conv1 = tf.nn.relu(conv1)

    # SOLUTION: Pooling. Input = 28x28x6. Output = 14x14x6.
    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')

    # SOLUTION: Layer 2: Convolutional. Output = 10x10x16.
    conv2_W = tf.Variable(tf.truncated_normal(shape=(5, 5, 6, 16), mean=mu, stddev=sigma))
    conv2_b = tf.Variable(tf.zeros(16))
    conv2 = tf.nn.conv2d(conv1, conv2_W, strides=[1, 1, 1, 1], padding='VALID') + conv2_b

    # SOLUTION: Activation.
    conv2 = tf.nn.relu(conv2)

    # SOLUTION: Pooling. Input = 10x10x16. Output = 5x5x16.
    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')

    # SOLUTION: Flatten. Input = 5x5x16. Output = 400.
    fc0 = flatten(conv2)

    # SOLUTION: Layer 3: Fully Connected. Input = 400. Output = 200.
    fc1_W = tf.Variable(tf.truncated_normal(shape=(400, 200), mean=mu, stddev=sigma))
    fc1_b = tf.Variable(tf.zeros(200))
    fc1 = tf.matmul(fc0, fc1_W) + fc1_b

    # SOLUTION: Activation.
    fc1 = tf.nn.relu(fc1)

    # SOLUTION: Layer 4: Fully Connected. Input = 200. Output = 200.
    fc2_W = tf.Variable(tf.truncated_normal(shape=(200, 200), mean=mu, stddev=sigma))
    fc2_b = tf.Variable(tf.zeros(200))
    fc2 = tf.matmul(fc1, fc2_W) + fc2_b

    # SOLUTION: Activation.
    fc2 = tf.nn.relu(fc2)

    # SOLUTION: Layer 5: Fully Connected. Input = 200. Output = 147.
    fc3_W = tf.Variable(tf.truncated_normal(shape=(200, 147), mean=mu, stddev=sigma))
    fc3_b = tf.Variable(tf.zeros(147))
    logits = tf.matmul(fc2, fc3_W) + fc3_b

    return logits

# Create placeholders for training data,
# x is a placeholder for a batch of input images. y is a placeholder for a batch of output labels.
x = tf.placeholder(tf.float32, (None, 32, 32, 1))
logits = LeNet(x)

saver = tf.train.Saver()

# load checkpoint and make prediction

with tf.Session() as sess:

    # read one input image from disk
    im = cv2.imread('test3.jpg')
    im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
    im = cv2.resize(im, (32, 32), interpolation=cv2.INTER_NEAREST)
    im = 255 - im  # for jpeg white is 255 black is 0, we revert so that white is 0 and black is 255
    im = np.divide(im.astype(np.float32), 255)

    # expand dims for input image
    im = np.expand_dims(im, axis=-1)
    im = np.expand_dims(im, axis=0)

    # restore training session and make prediction
    saver.restore(sess, tf.train.latest_checkpoint('.'))

    prediction = sess.run(logits, feed_dict={x: im})

    # sorted indices
    sorted_index = np.argsort(-prediction)
    print sorted_index

Saturday, 10 June 2017

Prepare data in MNIST TensorFlow LeNet tutorial format

Let's say we have a custom dataset of 146 character classes, and we want to train a LeNet to recognize these characters. We put the image files in separate train/test directories, and for train and test we have subdirectories 0,1,2,3,...,145. Image filenames are in this format: ['train' or 'test']/[0-145]/any_name.jpg. First, create read_data.py as follows so that we can read the images and labels, and save them to npy files:
import os
import numpy as np
import cv2
import matplotlib.pyplot as plt

def read_data(path):
    im_list = []
    label_list = []
    for (dirpath, dirnames, filenames) in os.walk(path):
        print('Processing ' + dirpath + ' ...')
        for filename in filenames:
            if filename.endswith('.jpg'):
                fullfile = os.sep.join([dirpath, filename])
                im = cv2.imread(fullfile)
                if im is None:
                    print(' WARN: ' + filename + ' in ' + dirpath + ' is bad!')
                    continue
                im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
                im = cv2.resize(im, (32, 32), interpolation=cv2.INTER_NEAREST)
                # plt.imshow(im)
                im = 255 - im # for jpeg white is 255 black is 0, we revert so that white is 0 and black is 255
                im = np.divide(im.astype(np.float32), 255)
                im = np.expand_dims(im, 2) # add an additional dimension (i.e., from 28x28 to 28x28x1)
                im_list.append(im)
                label = int(os.path.split(dirpath)[-1])
                label_list.append(np.uint8(label)) # 0 to 145, so uint8 is fine (0-255)
    im_array = np.array(im_list)
    label_array = np.array(label_list)
    return (im_array, label_array)
if __name__ == "__main__":
    #base_dir = '/home/twang/data/hcr/single_char/train/'
    #(im_array, label_array) = read_data(base_dir)
    #np.save(base_dir + 'trainData', im_array)
    #np.save(base_dir + 'trainLabel', label_array)

    base_dir = '/home/twang/data/hcr/single_char/test/'
    (im_array, label_array) = read_data(base_dir)
    np.save(base_dir + 'testData', im_array)
    np.save(base_dir + 'testLabel', label_array)
The training and evaluation scripts are then given by (as per the TensorFlow tutorial and this project):
import tensorflow as tf
from tensorflow.contrib.layers import flatten
import numpy as np
from sklearn.utils import shuffle

EPOCHS = 10
BATCH_SIZE = 128


def LeNet(x):
    # Hyperparameters
    mu = 0
    sigma = 0.1

    # SOLUTION: Layer 1: Convolutional. Input = 32x32x1. Output = 28x28x6.
    conv1_W = tf.Variable(tf.truncated_normal(shape=(5, 5, 1, 6), mean=mu, stddev=sigma))
    conv1_b = tf.Variable(tf.zeros(6))
    conv1 = tf.nn.conv2d(x, conv1_W, strides=[1, 1, 1, 1], padding='VALID') + conv1_b

    # SOLUTION: Activation.
    conv1 = tf.nn.relu(conv1)

    # SOLUTION: Pooling. Input = 28x28x6. Output = 14x14x6.
    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')

    # SOLUTION: Layer 2: Convolutional. Output = 10x10x16.
    conv2_W = tf.Variable(tf.truncated_normal(shape=(5, 5, 6, 16), mean=mu, stddev=sigma))
    conv2_b = tf.Variable(tf.zeros(16))
    conv2 = tf.nn.conv2d(conv1, conv2_W, strides=[1, 1, 1, 1], padding='VALID') + conv2_b

    # SOLUTION: Activation.
    conv2 = tf.nn.relu(conv2)

    # SOLUTION: Pooling. Input = 10x10x16. Output = 5x5x16.
    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')

    # SOLUTION: Flatten. Input = 5x5x16. Output = 400.
    fc0 = flatten(conv2)

    # SOLUTION: Layer 3: Fully Connected. Input = 400. Output = 200.
    fc1_W = tf.Variable(tf.truncated_normal(shape=(400, 200), mean=mu, stddev=sigma))
    fc1_b = tf.Variable(tf.zeros(200))
    fc1 = tf.matmul(fc0, fc1_W) + fc1_b

    # SOLUTION: Activation.
    fc1 = tf.nn.relu(fc1)

    # SOLUTION: Layer 4: Fully Connected. Input = 200. Output = 200.
    fc2_W = tf.Variable(tf.truncated_normal(shape=(200, 200), mean=mu, stddev=sigma))
    fc2_b = tf.Variable(tf.zeros(200))
    fc2 = tf.matmul(fc1, fc2_W) + fc2_b

    # SOLUTION: Activation.
    fc2 = tf.nn.relu(fc2)

    # SOLUTION: Layer 5: Fully Connected. Input = 200. Output = 146.
    fc3_W = tf.Variable(tf.truncated_normal(shape=(200, 146), mean=mu, stddev=sigma))
    fc3_b = tf.Variable(tf.zeros(146))
    logits = tf.matmul(fc2, fc3_W) + fc3_b

    return logits

def evaluate(X_data, y_data):
    num_examples = len(X_data)
    total_accuracy = 0
    sess = tf.get_default_session()
    for offset in range(0, num_examples, BATCH_SIZE):
        batch_x, batch_y = X_data[offset:offset+BATCH_SIZE], y_data[offset:offset+BATCH_SIZE]
        accuracy = sess.run(accuracy_operation, feed_dict={x: batch_x, y: batch_y})
        total_accuracy += (accuracy * len(batch_x))
    return total_accuracy / num_examples

# mnist = input_data.read_data_sets("MNIST_data/", reshape=False)
# X_train, y_train           = mnist.train.images, mnist.train.labels
# X_validation, y_validation = mnist.validation.images, mnist.validation.labels
# X_test, y_test             = mnist.test.images, mnist.test.labels
X_train = np.load('/home/twang/data/hcr/single_char/train/trainData.npy')
y_train = np.load('/home/twang/data/hcr/single_char/train/trainLabel.npy')
X_test = np.load('/home/twang/data/hcr/single_char/test/testData.npy')
y_test = np.load('/home/twang/data/hcr/single_char/test/testLabel.npy')

assert(len(X_train) == len(y_train))
assert(len(X_test) == len(y_test))

print()
print("Image Shape: {}".format(X_train[0].shape))
print()
print("Training Set:   {} samples".format(len(X_train)))
print("Test Set:       {} samples".format(len(X_test)))

print("Image Shape: {}".format(X_train[0].shape))

# Shuffule training data
X_train, y_train = shuffle(X_train, y_train)

# Create placeholders for training data,
# x is a placeholder for a batch of input images. y is a placeholder for a batch of output labels.
x = tf.placeholder(tf.float32, (None, 32, 32, 1))
y = tf.placeholder(tf.int32, (None))
one_hot_y = tf.one_hot(y, 146)

# training pipeline
rate = 0.001

logits = LeNet(x)
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=one_hot_y)
loss_operation = tf.reduce_mean(cross_entropy)
optimizer = tf.train.AdamOptimizer(learning_rate = rate)
training_operation = optimizer.minimize(loss_operation)

# evaluation
correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(one_hot_y, 1))
accuracy_operation = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
saver = tf.train.Saver()

# Training
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    num_examples = len(X_train)

    print("Training...")
    print()
    for i in range(EPOCHS):
        X_train, y_train = shuffle(X_train, y_train)
        for offset in range(0, num_examples, BATCH_SIZE):
            end = offset + BATCH_SIZE
            batch_x, batch_y = X_train[offset:end], y_train[offset:end]
            sess.run(training_operation, feed_dict={x: batch_x, y: batch_y})

        test_accuracy = evaluate(X_test, y_test)
        print("EPOCH {} ...".format(i + 1))
        print("Test Accuracy = {:.3f}".format(test_accuracy))
        print()

    saver.save(sess, 'lenet')
    print("Model saved")

with tf.Session() as sess:
    saver.restore(sess, tf.train.latest_checkpoint('.'))

    test_accuracy = evaluate(X_test, y_test)
    print("Test Accuracy = {:.3f}".format(test_accuracy))
Note that we have changed the dimensions of the last few layers (in the original LeNet, there are 10 classes, i.e., digits 0 to 9) to reflect the fact that we are working with a dataset of 146 classes.

Saturday, 15 April 2017

Split contents of a file according to a list

The following script runs through a list in a first file, and check through a second file for lines that start with items in the list. It outputs the matched lines in the second file to a third file.
#!/bin/bash

while read p; do
  grep "^$p" $2 >> $3
done <$1
The script is useful if you have a dataset split file and wanted to extract relevant lines from another file (e.g., a detector's bounding box outputs) according to this split. For example, you may want to run
./split_result.sh train.txt output_trainval.txt output_train.txt
./split_result.sh val.txt output_trainval.txt output_val.txt
Note the script makes no assumption about the ordering of lines in the second file.

Thursday, 12 January 2017

Reporting per-class accuracy with MatCaffe

With Caffe, the per-class accuracy output as specified in prototxt files seems to be buggy. Namely, if a particular class has no sample in a particular batch, the accuracy for that class will be set to zero for that particular batch. This is not a problem itself, but it seems these zero values will be counted towards calculating the average per-class accuracy, which is very misleading. See this PR for more information.

Use the following MATLAB code (you must compile MatCaffe first) for computing per-class accuracy on a validation or test set, instead.
% global parameters
DATA_ROOT = '/home/twang/data/flower/flower_531_crop_256/';
TEST_LIST_FILE = '/home/twang/data/flower/flower_531_meta/val.txt';
NUM_CLASSES = 531;

% caffe initialization
gpu_id = 0;
model = '/home/twang/caffe/models/resnet_flower531/deploy.prototxt';
weights = '/home/twang/caffe/models/resnet_flower531/resnet_flower531_iter_120000.caffemodel';

caffe.set_mode_gpu();
caffe.set_device(gpu_id);

net = caffe.Net(model, weights, 'test');

mean_data = caffe.io.read_mean('/home/twang/caffe/models/resnet_flower531/flower_mean.binaryproto');

% read files

[files,labels] = textread(TEST_LIST_FILE, '%s %d\n');
accuracy = zeros(2, NUM_CLASSES);

for ii = 0 : NUM_CLASSES-1
    class_files = files(labels == ii); % all files for current class
    accuracy(1, ii+1) = length(class_files);
    for jj = 1 : accuracy(1, ii+1)
        im_data = caffe.io.load_image([DATA_ROOT class_files{jj}]);
        input_data = {imresize(im_data - mean_data, [224 224])};
        scores = net.forward(input_data);
        [~, predict] = max(scores{1});
        if (predict == ii+1)
            accuracy(2, ii+1) = accuracy(2, ii+1) + 1;
        end
    end
    fprintf('Class #%03d accuracy = %.2f.\n', ii+1, accuracy(2, ii+1) / accuracy(1, ii+1));
end

caffe.reset_all();

Tuesday, 22 November 2016

Use Faster RCNN and ResNet codes for object detection and image classification with your own training data

I have recently uploaded two repositories to GitHub, both based on publicly available codes for state-of-the-art (1) object detection and (2) image classification. I would like to leave a few notes here, though.

(1) Faster RCNN for object detection (GitHub Link).

You can use your own PASCAL VOC formatted data to train an object detector. Check out how to alter the network parameters as shown in the example files located in:
person_detection_voc2012/py-faster-rcnn/models/pascal_voc/ZF/faster_rcnn_alt_opt/*.pt
In particular, you want to change the following settings in stage1_fast_rcnn_train.pt and stage2_fast_rcnn_train.pt:
num_class:2 # in our example, person detection only has two classes: person vs background
In cls_score -- num_output:2
In bbox_pred -- num_output:8 # this value is 4*num_class
Also in stage1_rpn_train.pt and stage2_rpn_train.pt:
num_class:2
Finally, in fast_rcnn_test.pt:
In cls_score -- num_output:2
In bbox_pred -- num_output:8 # this value is 4*num_class
Additionally, you need to modify lib/datasets/pascal_voc.py:
self._classes = ('__background__', # always index 0
                 'person')
And then recompile from python prompt:
importpy_compile
py_compile.compile(r'pascal_voc.py')
You can then follow instructions from this page to train your model.

(2) Fine tuning ResNet for image classification (GitHub Link).

This one is simple to use, and you may check this out before attempting to fine tune a ResNet model.

Example scripts can be found in: finetune-resnet-flower/caffe/examples/flower463/

Network parameters can be found in: finetune-resnet-flower/caffe/models/resnet_flower463/

Note that the parameters in solver50.prototxt may not be optimal (at least for my task at hand). For better performance (of course, slower training), you can try to increase stepsize as shown below:
test_iter: 2000
test_interval: 1000
base_lr: 0.001
lr_policy: "step"
gamma: 0.1
stepsize: 100000
display: 500
max_iter: 1000000
momentum: 0.9
weight_decay: 0.0005
Also, set the batch size appropriately to reflect the graphic memory capability of your system.

Thursday, 18 February 2016

Batch extract tarballs into directories with their respective base names (for ILSVRC 2012 training images)

I encountered this problem when I downloaded the ILSVRC 2012 training images. Thanks to this code, we now have a quick solution.

1. Paste the following code to file tarsubdir.sh:
#!/bin/sh
#usage:
# nameOfScript myArchive.tar.gz
# nameOfScript myArchive.gz
# nameOfScript myArchive.tar
#
# Result:
# myArchive   //folder
fileName="${1%.*}" #extracted filename
echo "file name is: $fileName"
trailingExtension="${fileName##*.}"
if [ "$trailingExtension" == "tar" ]
then
    fileName="${fileName%.*}"
    echo "new file name is: $fileName"
fi
echo "now file name is: $fileName"
mkdir "$fileName"
tar -xf "$1" --strip-components=0 -C "$fileName"
2. Paste the following code to another file untarall.sh:
#!/bin/sh

for file in *.tar
do
  bash ./tarsubdir.sh "$file"
done
3. Run from the bash prompt:
bash ./untarall.sh

Monday, 28 October 2013

Annotated feature convolution code from Deformable Parts Model

The following code is an excerpt from fconv_var_dim.cc that comes with the following paper:

A Discriminatively Trained, Multiscale, Deformable Part Model.
P. Felzenszwalb, D. McAllester, D. Ramaman.
Proceedings of the IEEE CVPR 2008.

The original code was downloaded from: http://people.cs.uchicago.edu/~pff/latent/

I only added some comments to make it easier to read. The comments illustrate how the data structures are designed (should also help understanding the multi-threaded and SSE-optimized implementations).
#include "mex.h"
#include <math.h>
#include <string.h>

/*
 * This code is used for computing filter responses.  It computes the
 * response of a set of filters with a feature map.  
 *
 * Basic version, relatively slow but very compatible.
 */

/*

The dimensions for A, B and C are as follows. For example:

A = A1*A2*32 single
B <2x1 cell> for each cell B{i} = B1i*B2i*32 single

Usually A is larger than B (as A is the feature map and B is the filter).

C <1x2 cell> for i-th cell is the response for the i-th filter.

C{i} = (A1-B1i+1)*(A2-B2i+1)*32 double

*/

struct thread_data {
  float *A;
  float *B;
  double *C;
  mxArray *mxC;
  const mwSize *A_dims;
  const mwSize *B_dims;
  mwSize C_dims[2];
};

// convolve A and B (A is feature map, B is filter)
void process(void *thread_arg) {
  thread_data *args = (thread_data *)thread_arg;
  float *A = args->A;
  float *B = args->B;
  double *C = args->C;
  const mwSize *A_dims = args->A_dims;
  const mwSize *B_dims = args->B_dims;
  const mwSize *C_dims = args->C_dims;
  int num_features = args->A_dims[2];

  // for each feature dimension (e.g., 32 dim HOG)
  for (int f = 0; f < num_features; f++) {
    double *dst = C;
    // calibrate the pointer to the base at the f-th dimension
    float *A_src = A + f*A_dims[0]*A_dims[1];      
    float *B_src = B + f*B_dims[0]*B_dims[1];
    // for each pixel (x,y) on the response map
    for (int x = 0; x < C_dims[1]; x++) {
      for (int y = 0; y < C_dims[0]; y++) {
        double val = 0;
        // operate along the second dimension (column) of B (filter)
        // xp is the pointer/offset for the second dimension of B
        for (int xp = 0; xp < B_dims[1]; xp++) {
          // calibrate the pointer to the current column
          // for A it's (x+xp)*(number of elements per column) + (row offset)
          float *A_off = A_src + (x+xp)*A_dims[0] + y;
          // for B it's xp*(number of elements per column)
          float *B_off = B_src + xp*B_dims[0];
          // depending on the number of elements per row
          // if number of elements per row <= 20 then use the loop-less code below
          // the more general default case which uses for-loop follows
          // as for most filters number of elements per row <= 20
          // this would acclerate the process.
          switch(B_dims[0]) {
            case 20: val += A_off[19] * B_off[19];
            case 19: val += A_off[18] * B_off[18];
            case 18: val += A_off[17] * B_off[17];
            case 17: val += A_off[16] * B_off[16];
            case 16: val += A_off[15] * B_off[15];
            case 15: val += A_off[14] * B_off[14];
            case 14: val += A_off[13] * B_off[13];
            case 13: val += A_off[12] * B_off[12];
            case 12: val += A_off[11] * B_off[11];
            case 11: val += A_off[10] * B_off[10];
            case 10: val += A_off[9] * B_off[9];
            case 9: val += A_off[8] * B_off[8];
            case 8: val += A_off[7] * B_off[7];
            case 7: val += A_off[6] * B_off[6];
            case 6: val += A_off[5] * B_off[5];
            case 5: val += A_off[4] * B_off[4];
            case 4: val += A_off[3] * B_off[3];
            case 3: val += A_off[2] * B_off[2];
            case 2: val += A_off[1] * B_off[1];
            case 1: val += A_off[0] * B_off[0];
              break;
            default:            
              for (int yp = 0; yp < B_dims[0]; yp++) {
                val += *(A_off++) * *(B_off++);
              }
         }
       }
       *(dst++) += val;
      }
    }
  }
}

// matlab entry point
// C = fconv(A, cell of B, start, end);
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 
  if (nrhs != 4)
    mexErrMsgTxt("Wrong number of inputs"); 
  if (nlhs != 1)
    mexErrMsgTxt("Wrong number of outputs");

  // get A
  const mxArray *mxA = prhs[0];
  if (mxGetNumberOfDimensions(mxA) != 3 || 
      mxGetClassID(mxA) != mxSINGLE_CLASS)
    mexErrMsgTxt("Invalid input: A");

  // get B and start/end
  const mxArray *cellB = prhs[1];
  mwSize num_bs = mxGetNumberOfElements(cellB);  
  int start = (int)mxGetScalar(prhs[2]) - 1;
  int end = (int)mxGetScalar(prhs[3]) - 1;
  if (start < 0 || end >= num_bs || start > end)
    mexErrMsgTxt("Invalid input: start/end");
  int len = end-start+1;

  // output cell
  plhs[0] = mxCreateCellMatrix(1, len);

  // do convolutions
  thread_data td;
  const mwSize *A_dims = mxGetDimensions(mxA);
  float *A = (float *)mxGetPr(mxA);
  for (int i = 0; i < len; i++) {
    const mxArray *mxB = mxGetCell(cellB, i+start);
    td.A_dims = A_dims;
    td.A = A;
    td.B_dims = mxGetDimensions(mxB);
    td.B = (float *)mxGetPr(mxB);
    if (mxGetNumberOfDimensions(mxB) != 3 ||
        mxGetClassID(mxB) != mxSINGLE_CLASS ||
        td.A_dims[2] != td.B_dims[2])
      mexErrMsgTxt("Invalid input: B");

    // compute size of output
    int height = td.A_dims[0] - td.B_dims[0] + 1;
    int width = td.A_dims[1] - td.B_dims[1] + 1;
    if (height < 1 || width < 1)
      mexErrMsgTxt("Invalid input: B should be smaller than A");
    td.C_dims[0] = height;
    td.C_dims[1] = width;
    td.mxC = mxCreateNumericArray(2, td.C_dims, mxDOUBLE_CLASS, mxREAL);
    td.C = (double *)mxGetPr(td.mxC);
    process((void *)&td);
    mxSetCell(plhs[0], i, td.mxC);
  }
}

Wednesday, 16 October 2013

Installing Linux Mint 13 Maya from hard drive in Windows 7

Here are some simple steps you need to follow if you want to install Linux Mint from your hard drive, without using USB sticks or blank DVDs. We assume you have Windows 7 installed.

1. Download Linux Mint 13 ISO from the official website. Put the ISO file at C:\.

2. Search for and download Grub4dos. Install Grub4dos by copying grldr to C:\. Create a new file C:\menu.lst and put the following contents in:
timeout 10
default 0

title Windows 7
find --set-root --ignore-floppies --ignore-cd /bootmgr
chainloader /bootmgr
3. Search for and download Bootice. Run Bootice and select your hard drive, then click on Process MBR. Choose Grub for DOS and click on Install/Config. This replaces your MBR with the one from Grub4dos.

4. Extract vmlinuz and initrd.lz files from your Linux Mint ISO and put them at C:\. They should be in a folder named casper. It is important that these files should be copied to your hard drive.

5. Restart your machine. You should be able to see the GRUB menu. From that press c for command line. Enter the following commands (for the first command you can press Tab key while entering to see list of files):
find /linuxmint-13-mate-dvd-64bit.iso
kernel (hd0,1)/vmlinuz boot=casper iso-scan/filename=/linuxmint-13-mate-dvd-64bit.iso
initrd (hd0,1)/initrd.lz
boot
When you enter the first command you should see the partition with your Linux Mint ISO file. For all the rest commands you need to change the (hd0,1) part accordingly. Also, you can also use --set-root option to set your root to that partition but that is not necessary.

6. Once Linux Mint Live is booted, go to the terminal and run
sudo umount -l /isodevice
This will avoid errors when you attempt to modify partitions. You can then click on the shortcut on the desktop to install Linux Mint to your hard drive.

Sunday, 13 October 2013

Infinite error loop when using MATLAB with nohup

If you run MATLAB scripts remotely with ssh and nohup (to keep it running in the background), you may find MATLAB entering an infinite loop of the following errors when your script or function finishes:

Warning: Error reading character from command line

and

Bad file descriptor

This has nothing to do with your code, just that you will have to add "</dev/null" before the "&" symbol which is causing the error. For example, you can run a function like this:
nohup matlab -nodisplay -r "myfunc_in_current_dir('params')" > ./mylog.txt </dev/null &
Also, people suggested using the "-nojvm" option. If you are plotting in your script then this option should be avoided.

Friday, 4 October 2013

Password-less ssh logins with RSA key pairs

First, edit your local ~/.bashrc file and add the following lines:
function server1(){
  ssh user@server.com
}
This creates a command named server1 to automatically ssh log in to server.com. However you will still need to enter your password each time you run this command.

Then, run the following commands from the bash shell on your local machine:
ssh-keygen -t rsa
ssh user@server.com "mkdir .ssh"
scp ~/.ssh/id_rsa.pub user@server.com:.ssh/id_rsa.pub
For the first command, press enter three times to create public and private keys without a password. The second command creates a directory named ".ssh" in your remote home directory. The third one copies over the public key.

Finally, run the following commands on the remote machine (by logging on using ssh):
touch ~/.ssh/authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
If the file ~/.ssh/authorized_keys already exists you can skip the first command. The second command appends id_rsa.pub into authorized_keys.

That's it. Try to type server1 again from your local shell prompt and you'll not be prompted to enter password from now on.

Change MAC address for your Ethernet adapter in Linux

Run the following commands from bash shell (yes you may need to cd to /etc first):
cd /etc
sudo vim rc.local
Add the following lines before "exit 0" (change the MAC address as needed):
/sbin/ifconfig eth2 down
/sbin/ifconfig eth2 hw ether 00:00:00:00:00:00
/sbin/ifconfig eth2 up
Restart your system and it should be OK.

If you want to temporarily change your MAC address, simply run the 3 lines of commands at bash shell prompt.

Converting 16-bit images to 8-bit images in OpenCV

Here is a simple example to convert 16-bit images to 8-bit images in OpenCV (for a single pixel). Note that the conversion is lossy.
// the original depth image is 16-bit (IPL_DEPTH_16U)
IplImage *dimg_original = cvLoadImage( filename, CV_LOAD_IMAGE_UNCHANGED );
CvScalar s = cvGet2D(dimg_original, 0, 0);
printf("16-bit depth value is %d, ",(int)s.val[0]);

// convert the original 16-bit depth image into 8-bit
IplImage *dimg = cvCreateImage(cvGetSize(dimg_original), IPL_DEPTH_8U,dimg_original->nChannels);
cvConvertScale(dimg_original, dimg, 1./40); //40 is the scaling factor to map 16-bit into 8-bit
CvScalar s1 = cvGet2D(dimg, 0, 0);
printf("8-bit depth value is %d\n",(int)s1.val[0]);
A few notes:
1. index in cvGet2D() and cvSet2D() starts with zero, and is in column-major order, i.e., cvGet2D(img, y, x)
2. If the scaling factor is greater than 1./256 (note that 65536 / 256 = 256, in the above example we set the factor to 1./40), pixels with larger values will all be mapped to 255 (no difference any more for larger values).
3. This method can also be used to convert 32-bit images to 8-bit images.

MATLAB error message during startup on Linux

If you install your MATLAB at /usr/local/, you may not be able to start MATLAB (getting a long error message saying that MATLAB is exiting with a fatal error and no GUI shows up).

Alternatively MATLAB may start with the error message "The desktop configuration was not saved successfully".

In these cases you could try to change the owner of your ~/.matlab folder (change twang to your Linux username):
sudo chown -R twang ~/.matlab
This should fix the problem.

Improved HOG feature in Deformable Parts Model (CVPR 2008)

The following code is an excerpt from features.cc that comes with the following paper:

A Discriminatively Trained, Multiscale, Deformable Part Model.
P. Felzenszwalb, D. McAllester, D. Ramaman.
Proceedings of the IEEE CVPR 2008.

The original code was downloaded from: http://people.cs.uchicago.edu/~pff/latent/

I only added some comments to make it easier to read. This improved HOG feature performs a little bit better than the original HOG implementation in OpenCV. It is easy to understand, tweak, and very popular in the community.
#include <math.h>
#include "mex.h"

// small value, used to avoid division by zero
#define eps 0.0001

// unit vectors used to compute gradient orientation
double uu[9] = {1.0000, 
0.9397, 
0.7660, 
0.500, 
0.1736, 
-0.1736, 
-0.5000, 
-0.7660, 
-0.9397};
double vv[9] = {0.0000, 
0.3420, 
0.6428, 
0.8660, 
0.9848, 
0.9848, 
0.8660, 
0.6428, 
0.3420};

static inline double min(double x, double y) { return (x <= y ? x : y); }
static inline double max(double x, double y) { return (x <= y ? y : x); }

static inline int min(int x, int y) { return (x <= y ? x : y); }
static inline int max(int x, int y) { return (x <= y ? y : x); }

// main function:
// takes a double color image and a bin size 
// returns HOG features
mxArray *process(const mxArray *mximage, const mxArray *mxsbin) {
  double *im = (double *)mxGetPr(mximage);
  const int *dims = mxGetDimensions(mximage);
  if (mxGetNumberOfDimensions(mximage) != 3 ||
      dims[2] != 3 || // must be a 3-channel image
      mxGetClassID(mximage) != mxDOUBLE_CLASS)
    mexErrMsgTxt("Invalid input");

  int sbin = (int)mxGetScalar(mxsbin); // pixel size for the HOG cells

  // memory for caching orientation histograms & their norms
  int blocks[2];
  blocks[0] = (int)round((double)dims[0]/(double)sbin); // how many cells along the cloumn
  blocks[1] = (int)round((double)dims[1]/(double)sbin); // how many cells along the row
  double *hist = (double *)mxCalloc(blocks[0]*blocks[1]*18, sizeof(double)); // level histogram into 18 orientation bins
  double *norm = (double *)mxCalloc(blocks[0]*blocks[1], sizeof(double)); // store the norm for gradient in each cell

  // memory for HOG features
  int out[3];
  out[0] = max(blocks[0]-2, 0);
  out[1] = max(blocks[1]-2, 0);
  out[2] = 27+4+1; 
  mxArray *mxfeat = mxCreateNumericArray(3, out, mxDOUBLE_CLASS, mxREAL);
  double *feat = (double *)mxGetPr(mxfeat);
  
  int visible[2];
  visible[0] = blocks[0]*sbin; // number of pixels along cloumn
  visible[1] = blocks[1]*sbin; // number of pixels along row
  
  for (int x = 1; x < visible[1]-1; x++) { // NOTE: in this loop row comes first, and x and y are iterated from 1 to visible[]-1,
    for (int y = 1; y < visible[0]-1; y++) { // in this way both x and y cooridnates leave out 1 pixel at the edges

      // first color channel
      double *s = im + min(x, dims[1]-2)*dims[0] + min(y, dims[0]-2); // NOTE: matlab matrices are stored in cloumns, to get access to rows 
      // we need to multiply by dims[0] (number of cells along the cloumn)
      double dy = *(s+1) - *(s-1);
      double dx = *(s+dims[0]) - *(s-dims[0]);
      double v = dx*dx + dy*dy;

      // second color channel
      s += dims[0]*dims[1]; // moves to the next channel
      double dy2 = *(s+1) - *(s-1);
      double dx2 = *(s+dims[0]) - *(s-dims[0]);
      double v2 = dx2*dx2 + dy2*dy2;

      // third color channel
      s += dims[0]*dims[1]; // moves to the next channel
      double dy3 = *(s+1) - *(s-1);
      double dx3 = *(s+dims[0]) - *(s-dims[0]);
      double v3 = dx3*dx3 + dy3*dy3;

      // pick channel with strongest gradient
      if (v2 > v) {
        v = v2;
        dx = dx2;
        dy = dy2;
      } 
      if (v3 > v) {
        v = v3;
        dx = dx3;
        dy = dy3;
      }

      // snap to one of 18 orientations
      double best_dot = 0;
      int best_o = 0;
      for (int o = 0; o < 9; o++) {
        double dot = uu[o]*dx + vv[o]*dy;
        if (dot > best_dot) {
          best_dot = dot;
          best_o = o;
        } else if (-dot > best_dot) {
          best_dot = -dot;
          best_o = o+9;
        }
      }
      
      // add to 4 histograms around pixel using linear interpolation
      double xp = ((double)x+0.5)/(double)sbin - 0.5;
      double yp = ((double)y+0.5)/(double)sbin - 0.5;
      int ixp = (int)floor(xp);
      int iyp = (int)floor(yp);
      double vx0 = xp-ixp;
      double vy0 = yp-iyp;
      double vx1 = 1.0-vx0;
      double vy1 = 1.0-vy0;
      v = sqrt(v);

      if (ixp >= 0 && iyp >= 0) {
        *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 
         vx1*vy1*v;
      }

      if (ixp+1 < blocks[1] && iyp >= 0) {
        *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 
         vx0*vy1*v;
      }

      if (ixp >= 0 && iyp+1 < blocks[0]) {
        *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 
         vx1*vy0*v;
      }

      if (ixp+1 < blocks[1] && iyp+1 < blocks[0]) {
        *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 
         vx0*vy0*v;
      }
    }
  }

  // compute energy in each block by summing over orientations
  for (int o = 0; o < 9; o++) {
    double *src1 = hist + o*blocks[0]*blocks[1];
    double *src2 = hist + (o+9)*blocks[0]*blocks[1];
    double *dst = norm;
    double *end = norm + blocks[1]*blocks[0];
    while (dst < end) {
      *(dst++) += (*src1 + *src2) * (*src1 + *src2);
      src1++;
      src2++;
    }
  }

  // compute features
  for (int x = 0; x < out[1]; x++) {
    for (int y = 0; y < out[0]; y++) {
      double *dst = feat + x*out[0] + y;      
      double *src, *p, n1, n2, n3, n4;

      p = norm + (x+1)*blocks[0] + y+1;
      n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
      p = norm + (x+1)*blocks[0] + y;
      n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
      p = norm + x*blocks[0] + y+1;
      n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
      p = norm + x*blocks[0] + y;      
      n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);

      double t1 = 0;
      double t2 = 0;
      double t3 = 0;
      double t4 = 0;

      // contrast-sensitive features
      src = hist + (x+1)*blocks[0] + (y+1);
      for (int o = 0; o < 18; o++) {
        double h1 = min(*src * n1, 0.2);
        double h2 = min(*src * n2, 0.2);
        double h3 = min(*src * n3, 0.2);
        double h4 = min(*src * n4, 0.2);
        *dst = 0.5 * (h1 + h2 + h3 + h4);
        t1 += h1;
        t2 += h2;
        t3 += h3;
        t4 += h4;
        dst += out[0]*out[1];
        src += blocks[0]*blocks[1];
      }

      // contrast-insensitive features
      src = hist + (x+1)*blocks[0] + (y+1);
      for (int o = 0; o < 9; o++) {
        double sum = *src + *(src + 9*blocks[0]*blocks[1]);
        double h1 = min(sum * n1, 0.2);
        double h2 = min(sum * n2, 0.2);
        double h3 = min(sum * n3, 0.2);
        double h4 = min(sum * n4, 0.2);
        *dst = 0.5 * (h1 + h2 + h3 + h4);
        dst += out[0]*out[1];
        src += blocks[0]*blocks[1];
      }

      // texture features
      *dst = 0.2357 * t1;
      dst += out[0]*out[1];
      *dst = 0.2357 * t2;
      dst += out[0]*out[1];
      *dst = 0.2357 * t3;
      dst += out[0]*out[1];
      *dst = 0.2357 * t4;

      // truncation feature
      dst += out[0]*out[1];
      *dst = 0;
    }
  }

  mxFree(hist);
  mxFree(norm);
  return mxfeat;
}

// matlab entry point
// F = features(image, bin)
// image should be color with double values
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 
  if (nrhs != 2)
    mexErrMsgTxt("Wrong number of inputs"); 
  if (nlhs != 1)
    mexErrMsgTxt("Wrong number of outputs");
  plhs[0] = process(prhs[0], prhs[1]);
}

Thursday, 3 October 2013

Redirecting all traffic to add www prefix with Apache

It may be useful to add the "www." prefix automatically for all traffic if your visitors are not typing them. With Apache you need to have mod_rewrite enabled to use this method.

1. Allow mod_rewrite override for your current website. Go to your site configuration file (located at /etc/apache2/sites-enabled/000-default if you are using Ubuntu) and add the following lines into the <VirtualHost *:80> section:
<Directory /var/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride all
    Order allow,deny
    allow from all
</Directory>
2. Go to your document root (e.g., /var/www), create a new file named .htaccess and with the following content:
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
That's it. Restart Apache and it should work by now.

Installing JDK on Ubuntu

This is an example of how to install JDK on Ubuntu. I wrote this when I was using JDK 7 with Ubuntu 10.04 -- so you may want to check Oracle's website for the latest version.

1. Download one of the following files:
http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-i586.tar.gz (for 32-bit)
http://download.oracle.com/otn-pub/java/jdk/7u1-b08/jdk-7u1-linux-x64.tar.gz (for 64-bit)

2. Decompress the .tar.gz file.

3. Move the JDK files to /usr/lib/jvm/jdk1.7.0 or somewhere else as appropriate.

4. Run from bash shell:
sudo vim /etc/environment
You will see something like
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
Change the file to something like (also change the installation path if needed)
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/lib/jvm/jdk1.7.0/bin"
CLASSPATH=.:/usr/lib/jvm/jdk1.7.0/lib
JAVA_HOME=/usr/lib/jvm/jdk1.7.0
And then reload .bashrc file:
source ~/.bashrc
5. Run the following commands from bash shell:
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.7.0/bin/java 300
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.7.0/bin/javac 300
sudo update-alternatives --install /usr/bin/javap javap /usr/lib/jvm/jdk1.7.0/bin/javap 300
sudo update-alternatives --install /usr/bin/javadoc javadoc /usr/lib/jvm/jdk1.7.0/bin/javadoc 300
Installation is now complete. Run the following command from bash shell for a test:
java -version

Upgrading/compiling OpenCV on Linux from source code and use with Eclipse

In order to upgrade your OpenCV to the latest version (versions on APT are usually obsolete), or if you modified the source code in OpenCV and need to recompile it, you can follow the following steps as an example:

1. Download the latest source tarball from OpenCV’s website.

2. Extract the .tar.gz file to a directory, say ~/opencv_src. This is just a temporary directory for the source code.

3. Create another directory for build files, say ~/opencv_build. Change to this directory (do not stay in the source folder). Make the build files using CMAKE (you may change the options where necessary):
cmake -D CMAKE_BUILD_TYPE=RELEASE -D WITH_TBB=YES -D TBB_INCLUDE_DIRS=/usr/include/tbb -D CMAKE_INSTALL_PREFIX=/usr -D BUILD_PYTHON_SUPPORT=ON -D WITH_OPENNI=YES ../opencv_src
Note: You could refer to CMakeLists.txt in ~/opencv_src for more options (for example, to install OpenNI support you need to set WITH_OPENNI to YES). Another important thing is to check OpenCV website for required APT packages. Install those packages using apt-get. Otherwise you will get some error information when trying to run the above command.

4. Should the command is executed successfully, you will have the build files. Note that we have set CMAKE_INSTALL_PREFIX to /usr. This is by default /usr/local but if we need to replace the old versions that comes with APT, we need to set the prefix to /usr. This is where apt-get installs programs. For /usr/local, it is usually for self-maintained packages. So actually it is up to you to use /usr or /usr/local.

5. Type “make” and “sudo make install” to install the files to the desired location. If we set prefix to /usr then files will be copied to locations like /usr/lib, /usr/include/opencv. The make command will take a little while.

You should have installed OpenCV now. Following steps are for using OpenCV with Eclipse CDT.

6. Open Eclipse (with CDT installed), create a new C++ project. And then click on Menu -> Project -> Properties -> C/C++ Build -> Settings -> Tool Settings. We need to change:

(1) GCC C++ Compiler -> Includes. Add “/usr/include/opencv”

(2) GCC C++ Linker -> Library search path. Add “/usr/lib”

(3) GCC C++ Linker -> Libraries. Add “opencv_core” and “opencv_highgui” (for a complete list of libraries available, check the libopencv_***.so files located at /usr/lib. When adding their names, simply remove "lib" at front and any extensions. For example, use "opencv_core" for "libopencv_core.so").

7. To give it a go, use the following sample program:
#include <highgui.h>

int main( int argc, char** argv )
{

 IplImage* img = cvLoadImage( "/path/to/an/image.jpg" ); // change this to a valid filename
 cvNamedWindow( "Example1", CV_WINDOW_AUTOSIZE );
 cvShowImage( "Example1", img );
 cvWaitKey(0);
 cvReleaseImage( &img );
 cvDestroyWindow( "Example1" );

}