PHP in Elphel cameras

From ElphelWiki
Revision as of 06:00, 8 February 2012 by Dimitrios (talk | contribs) (Corrected parameter ELPHEL_FP100S in example)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Why PHP?

One of the main objectives of Elphel products is to make them developer-friendly. Just opening of the camera internals (including source code, FPGA code, hardware schematics) is not enough without providing means to modify the cameras easily, and here PHP comes to the rescue. It is powerful and efficient and is well known by many developers including those who may have problems to modify FPGA code or kernel drivers. It is much easier to try your custom applications written in PHP - just transfer the files to the camera (i.e. with ftp) and open them in the web browser.

PHP supports custom extensions written in C and we'll work (and I hope not just we at Elphel) to provide more custom function to combine flexibility and ease of use of the PHP with efficiency of native compiled code.

PHP in Elphel cameras (models 353/363)

Current version of PHP in Elphel cameras is 5.2.1, we will try to keep it (almost) current. PHP in the camera is working in Fast CGI mode through Lighttpd. Fast CGI mode does not restart applications (like traditional CGI does) for each HTTP request but rather keeps them alive to serve multiple requests. In the case of large application like PHP itself it makes a big performance difference and allows usage of the PHP scripts in the camera without the penalty of slow responses - something really undesirable for AJAX applications.

How to use PHP in the camera

It is very simple. It is the same as to try your HTML/JavaScript pages. You just need to ftp you script to one of the two locations (or their subdirectories) in the camera. Examples below assume you have the default camera IP=

  • /var/html (visible as in the web browser) - recommended for experiments, if something goes wrong you can just power-cycle the camera and all your changes will be gone
  • /usr/html' (visible as in the web browser) - permanent (until next full firmware upgrade ) storage in the camera flash memory. This second location is more dangerous as you can change the installed files and rebooting will not restore the default behaviour. Flash memory has limited number of write cycles and can develop additional bad blocks during usage, so this location is recommended for "final" files, already tested in /var/html.

You may also edit small text files with web-based editor. It does not allow to open non-existent file, but has an option to "Save As" - So you can try opening and then (modify and) save it to /var/html/test.php . When done - just open the following URL and watch the results of the script processing by in-camera PHP:  - run - edit

It is also possible to execute PHP scripts without the browser - camera startup scripts already use it for various initialization of the hardware. You can (silently) execute the same program in telnet session:

php /var/html/test.php >/dev/null

Elphel PHP extension in the camera

Since Elphel software version 7.1.6 there is a custom extension module for PHP installed in the camera, you may also create your own extension as described below. The existent extension exports several functions and defines constants.

Constants, defined in elphel PHP extension

see: Elphel_PHP_constants

elphel extension functions

Note: Currently arguments (and array values) are supposed to be numbers, not strings - you may want to use $value+0 (instead of just $value) if $value may be a string (i.e. passed from _GET[])--Andrey.filippov 19:55, 14 December 2007 (CST)


long elphel_get_P_value (long $address)

This function returns value of one of internal camera parameters, as defined in c313a.h and documented in Elphel_PHP_constants. It is strongly recommended to use symbolic names as the numeric values may change in the future (as they already did multiple times). The values are maintained by the camera driver(s), usually they are updated when the driver programs the acquisition process.


echo elphel_get_P_value(ELPHEL_FP1000S)/1000; // will return current camera sensor frame rate in (frames per second);


void elphel_set_P_value (long $address, long $data)

Set a single camera parameter.


elphel_set_P_value(ELPHEL_QUALITY,100); // set JPEG compression quality to 100% - virtually lossless

The elphel_get_P_value and elphel_set_P_value deal with different variables. Those that are read are validated to match the allowed values and may differ from those written (and they are only updated when sensor/compressor are programmed). You may read the parameters values that were written to the camera by adding ELPHEL_NUMBER to the parameter address, i.e

echo elphel_get_P_value(ELPHEL_QUALITY+ELPHEL_NUMBER); // will output the compression quality as written


array elphel_get_P_arr (array $template)

Read multiple camera parameters as an associative array, according to $template. $template should have elements with the key names matching the camera parameters names, same as defined Elphel_PHP_constants, but without "ELPHEL_" prefix (all the elements with the other keys will be ignored). elphel_get_P_arr returns array, where element keys are the same as in $template (after removing of any unknown ones), and values are long values read from the camera.


$arr=array("foo"=>"", "WOI_LEFT" => "",  "WOI_TOP" => "", "1", 3, "WOI_WIDTH" => 0, "WOI_HEIGHT" => 0);
print_r ($arr);

will return (5MPix sensor, full window):

   [WOI_LEFT] => 0
   [WOI_TOP] => 0
   [WOI_WIDTH] => 2592
   [WOI_HEIGHT] => 1936


long elphel_set_P_arr (array $input, long $frame, long $flag) )

Write multiple camera parameters, provided as an associative array, where keys are parameter names same as defined Elphel_PHP_constants, but without "ELPHEL_" prefix. Values should be numeric and integer, all other types are ignored.

optional arguments:

 frame - frame number to write these parameters to - if not specified default framedelay (current frame + 3) is applied)
 flag - possible flags? 

Return value - frame number to which parameters were written


echo elphel_set_P_arr(array("WOI_LEFT" => 896,  "WOI_TOP" => 672, "WOI_WIDTH" => 800, "WOI_HEIGHT" => 600));

Will set 800x600 window in the center of the 5Mpix sensor and print frame number these changes where applied to


long elphel_get_state (void) There are multiple states defined in the camera, some are obsolete, the following are most common (no symbolic constants in PHP yet)

  • 0 - "reset" (i.e. no images yet acquired after power up)
  • 7 - "stopped" - sensor is constantly acquiring images (used for autoexposure), but compressor is stopped and no frames get to the circbuf
  • 8 - "running" - compressor is acquiring images to circbuf
  • 9 - "stopping" - compressor is compressing the last frame (transition from "running" to "stopped"
  • 10 - "single_buffer" - compressor is acquiring full circbuf (plus one frame as it will notice the buffer is full after overrunning the original starting point) and then will stop


long elphel_fpga_read (long $address)

Reads data from a certain fpga register address.


echo elphel_fpga_read(4); // outputs the data at address 4


void elphel_fpga_write (long $address, long $data)

Writes data to a certain fpga register address.

for Elphel 353 cameras these values can be the following (quoted from x353.h):

 187 #define X313_WA_WCTL	    0 // write control register - 32
 188 #define X313_WA_DMACR	    1 // DMA control register:
 189                               // 17:    DMA enable
 190                               // 16:    0- raw, 1 - JPEG
 191                               // 15-10: not used
 192                               // 0-9:   line length in long words
 193 #define X313_WA_SENSFPN	    2 // Sensor/FPN control register:
 194                                        // [10] - testmode - if 1 generates gradient data (as Zoran chips) where pixel value = horizontal position
 195                                        // [9:7] - submode - subtract background (8 bits) mode (use di[7:0]):
 196                                        // 000 - no subtraction;
 197                                        // 001 - (finest) subtract 8 bit bkgnd from 12 bits pixels
 198                                        // 010 - shift 8bit bkgnd 1 bit  left before applying
 199                                        // 011 - shift 8bit bkgnd 2 bits left before applying
 200                                        // 100 - shift 8bit bkgnd 2 bits left before applying
 201                                        // 101 - shift 8bit bkgnd 2 bits left before applying
 202                                        // fpn data to subtract should be a little less to add a "fat zero"
 203                                        // [6:4] - mpymode sensitivity correction mode (use di[15:8]):
 204                                        // 000 - no correction
 205                                        // 001 - fine correction (+/-3.125%)
 206                                        // 010 - fine correction (+/-6.25%)
 207                                        // 011 - fine correction (+/-12.5%)
 208                                        // 100 - +/- 25%
 209                                        // 101 - +/- 50%
 210                                        // [3] - wdth - word width: 0 - 8 bit, 1 - 16 bit (5 MSB == 0)
 211                                        // [2:0] scaling of 11 bit FPN result to fit in 8bit output:
 212                                        // 00 - default - use [9:1]
 213                                        // 01 - use [10:2] - to protect from saturation after applying mpymode
 214                                        //      nominal range - 0..127
 215                                        // 10 - use [7:0] before saturation, "digital gain" == 4 (maximal)
 216                                        // 11 - use [8:1] before saturation, "digital gain" == 2
 218                               // 31-8: not used
 219                               //    7: 0 - normal, 1 - test mode (as Zoran)
 220                               //  6-5: subtract FPN mode:
 221                               //       0 - no subtraction
 222                               //       1 (fine) - subtract 8-bit FPN from 10-bit pixel
 223                               //       2 - multiply FPN by 2 before subtracting
 224                               //       3 - multiply FPN by 4 before subtracting (full scfpcfale)
 225                               // negative result is replaced by 0, decrease FPN data before applying for "fat 0"
 226                               // 4-3: muliply by inverse sensitivity (sensitivity correction) mode:
 227                               //       0 - no correction
 228                               //       1 - fine (+/- 12.5%)
 229                               //       2 - medium (+/- 25%)
 230                               //       3 - maximal (+/- 50%)
 231                               //   2: pixel depth:
 232                               //       0 - 8 bits/pixel (needed for JPEG encoding)
 233                               //       1 - 16 bits/pixel (only 11 LSBs are non-zero)
 234                               // 1-0: scale (8 bit mode only):
 235                               //       0 - full scale corresponds to sensor full scale if no correction was applied
 236                               //       1 - divide by 2. This will guarantee against saturation after sensitivity correction was
 237                               //           applied. Nominal output range - 0..127
 238                               //       2 - multiply by 4 (use 8 lower bits from sensor data)
 239                               //       3 - multiply by 2
 240                               // Result data is saturated by 255
 241 #define X313_WA_VIRTTRIG   3  // Virtual trigger threshold
 242                               //   31-22  not used
 243                               //   21-0  Trigger will fire if sum of pixels in a line
 244                               //         (after FPN processig) is more than this value.
 245                               //         Disabled if 0 - selected real (electrical) external trigger
 246 #define X313_WA_TRIG       4  // Sensor triggering
 247                               // 31-3: not used
 248                               //    2: Enable sensor (0 - abort at once)
 249                               //    1: External Trigger (0 - internal)
 250                               //    0: continuous acquisition (0 - single "frame" - actually number of lines specified)
 251 #define X313_WA_NLINES     5  //  number of lines to acquire (afer trigger)
 252                               // 31-11: not used
 253                               // 10-0 : number of lines to acquire (in a frame or after the external trigger)
 254 //#define X313_WA_IRQM       6  // Interrupt mask (1 - enable, 0 - reset all but done)
 255                               // 31-04: not used
 256                               //     3: Done. Reset by writing to X313_WA_TRIG
 257                               //     2: frame acquisition over
 258                               //     1: external trigger
 259                               //     0: frame sync (vacts)
 260 #define X313_WA_DCDC       7  // sensor DC-DC converter frequency settings
 261                               // 31-05: not used
 262                               //   4-0  0 - use internal clock (not sync)
 263                               //        1..31 use divided pixel clock
 264                               //        for 20MHz use 5'h10,
 265                               //        else - N= (Fpix[MHz]/1.2)-1, if Fpix=20MHz,
 266                               //        N= 15.7->16=5'h10


elphel_fpga_write(4, 4); // Sets sensor triggering to an unused number and therefore disabling it -> acquire single frame to video buffer


void elphel_reset_sensor (void)

Reset the sensor settings, stop compressor (if it was running) and force sensor re-initialization.




void elphel_program_sensor (long $nonstop)

Applies written earlier (see elphel_set_P_value, elphel_set_P_arr, elphel_white_balance) parameters to the cameras sensor and FPGA compressor. $nonstop determines if the acquisition should be restarted ($nonstop==0) or updated without stopping ($nonstop==1).

Note: As of version 8.x, parameters are applied as they are set. Therefore, this function is no longer necessary.




void elphel_wait_frame (void)

Waits for the next frame to be compressed to the circular buffer - it is also the time when some other parameters (accessible with elphel_get_P_value, elphel_get_P_arr) are updated.




long elphel_wait_frame (long frame)

wait for absolute frame number (includes those that are not compressed)

returns: frame number




long elphel_skip_frames (long frames)

skip some frames (includes those that are not compressed) - will work even if no frames are compressed

returns: frame number




void elphel_compressor_run (void)

Turn the FPGA compressor on. Sensor should be already programmed ([[PHP_in_Elphel_cameras#|elphel_get_state] should return 7 (or higher) if tested prior to issuing elphel_compressor_run() command.




void elphel_compressor_stop (void)

Stop camera compressor (will stop transferring more frames to the circular buffer. This command does not to be synchronized - compressor will finish current frame before stopping.




void elphel_compressor_frame (void)

Acquire one frame to the buffer (will apply written JPEG quality). Sensor should be already programmed - elphel_get_state should return 7 (or higher) if tested prior to issuing elphel_compressor_frame() command.




double elphel_set_fpga_time (double seconds)

Set FPGA internal timer that is used for time-stamping images and video frames. Internal counter has a 32-bit seconds counter and 12 bit microsecond one, so you may specify epoch - seconds since 01/01/1970.

Returns time set as a double - it should match the input parameter after rounding to microseconds (resolution of the FPGA timer).




double elphel_get_fpga_time (void)

Copies current value of the FPGA timer registers to the global array (maybe read with several functions described above) and returns the the time as a double value, combining second as microseconds. The readout process guaranties that seconds and microseconds are sample simultaneously.


echo elphel_get_fpga_time();


int elphel_white_balance ([double thrsh [, double minfrac [, double rscale [, double bscale]]]])

This function is designed to perform white balancing of the camera images. It relies on histogram calculation, "gamma" tables (curves) (both implemented in the FPGA) and the auto-exposure software in the camera (not yet included in this PHP extension, it is currently controlled through the separate CGI program). White balance uses the same window as auto-exposure, only data inside the specified rectangular window is processed.

The goal of this function is to make the brightest areas in the image white (equal values of red, green and blue), it assumes that auto-exposure algorithm (operating after each frame acquired) already adjusted the brightness of the images so it uses all the 8-bit range and some pixel values are close to the maximal 255.

The balancing is achieved by modification of the lookup tables ("gamma" tables, "curves") that map 12-bit (in 5MPix sensor, may be 10 or 14 bits with other devices) pixel data to the 8-bit pixel values used by the image/video compressor, the same values can be manually entered with elphel_get_P_value, elphel_get_P_arr functions applied to RSCALE and BSCALE parameters. The balancing is sensor-independent and it does not change sensor analog gains (where available), but if the required correction is significant, it is recommended to adjust those gains also, leaving only fine adjustment to the white balance algorithm - that will increase the dynamic range of the images.

All function parameters are optional, but you can not skip the first one(s) if you would like to specify the next one(s), in that case default values should be explicitly entered.

First parameter thrsh defines how bright should be pixels to be counted as white. Default value of 0.98 corresponds to 250 counts on 255 scale.

Second parameter minfrac (default 0.01) tells how many pixels (at least) should be counted, as a fraction of the total number of the pixels in the specified window. If there are too few pixels with the brightness above the thrsh, the thrsh will be lowered, to let more pixels to be counted.

Next two parameters - rscale and bscale (both with default values of 1.0) can be used to apply manual bias to the automatically calculated values of red and blue scales ( green)

When this function is executed successfully it returns 0 (negative numbers indicate failure) in adjusts parameters (similar to manual elphel_get_P_value, elphel_get_P_arr), but that will propagate to the FPGA only after elphel_program_sensor is executed. If you do not need to change any other parameters, you may use elphel_program_sensor(1) - in this case the image/video acquisition process will not be interrupted, frame timing will be preserved.

As this function uses the histograms calculated from the previously acquired images, you actually need to acquire some. It is recommended you wait till the auto-exposure will adjust the brightness before executing elphel_white_balance. It also is not needed to run it as frequently as auto-exposure (for each frame), usually it is enough to call it only when the lighting conditions are changed.


if (elphel_white_balance()>=0) elphel_program_sensor(1);


elphel_white_balance(0.98,0.01,1.0,1.2); // set bluish tint

whitebalance.php is an example script that uses this function. It is installed in the camera and can be called as (provided camera has the default IP of



void elphel_autoexposure_set (void)

This function controls auto-exposure in the camera. It is a quick modification that preserves the existent functionality (including interface) and just adds control from the PHP. This function itself does not take any parameters, it also does not return any data, It applies parameters that have to be already written by either of elphel_set_P_value, elphel_set_P_arr, the current state of the auto-exposure algorithm can be read back using elphel_get_P_value, elphel_get_P_arr functions.

auto-exposure software in the camera
kernel space (driver)

When the image from the sensor is acquired in the camera (even the compressor is off), software interrupt is generated and among others two things related to the auto-exposure control happen in the driver.

  • software prepares histograms calculated in the FPGA during the previous frame acquisition to be analysed by the user space application and make them available to the userspace applications through memory-mapped arrays (mmap) and
  • the previously calculated exposure is applied to the sensor to take effect during the next frame acquisition. Exposure time is the only parameter controlled automatically, you may also want to change analog gain(s) (sensitivity) to extend the range of the illumination levels that camera can handle. Most of this driver functionality is implemented in the hist353.c.
user space (daemon)

User space application autoexp.c (running as a daemon) processes the histograms and calculates the corrected value of the exposure, the result value is sent to the driver that applies the required correction.

In addition the histograms (four of them - individually for each of the 4 Bayer components, including 2 sets of green pixels), driver provides applications with the lookup ("gamma") tables that are used by the FPGA to convert sensor pixel value (12 bit for the 5MPix sensor) into the 8-bit range, usually - non-linearly, controlled by "gamma" parameter. This tables are needed when processing the histograms, as FPGA calculates them after the LUT are applied to the pixel values.

The auto-exposure configuration (input) parameters and the current state (output) can still be accessed using the current CGI script autoexpos.cgi.c (as used by camvc user interface), alternatively they are directly accessible through the elphel_set_P_value, elphel_set_P_arr, elphel_get_P_value, elphel_get_P_arr functions.

auto-exposure algorithm in the camera

The goal of the auto-exposure algorithm is to use as much as possible of the sensor dynamic range in the area of interest. It tries to adjust the exposure of the sensor so the whole range of the image pixel values (up to 255) are used (small number of pixels are usually still allowed to overexpose and be return the 255 limit value). It is possible to maintain the brightest pixels (disregarding just few "super-bright") near 255, but the it has 2 disadvantages:

  • sometimes with the back light you want to have good reproduction of darker objects while allowing the bright background to saturate
  • it is difficult to calculate required correction when the exposure is too long (some areas are over-saturated), because there is no way to know how much are the pixels saturated (if the image is too dim, the required exposure increase is just the ratio of 255 and the brightest pixels in the image)

For these reasons a slightly different algorithm is used - the software tries to maintain specified part of the pixels in the window of interest to be below the specified level (usually high, but less than the full scale). In this case if the brightness of the scene suddenly increases (up to the ratio of the full scale pixel value to the specified threshold), the exposure correction can be calculated and applied in a single step. You may experiment with this algorithm using GUI in camvc camera web interface.

parameters that control auto-exposure in the camera

The following are parameters (it is possible to both their names as array keys with functions elphel_set_P_arr, elphel_get_P_arr, elphel_get_P_warr. There are also constants defined that use the same names with "ELPHEL_"prefix (i.e. ELPHEL_AUTOEXP_ON), these constants are useful with the functions elphel_set_P_value, elphel_get_P_value. All the parameter values are long (32-bit) integers, you may read them back (as written) with elphel_get_P_warr function call. Te value of ELPHEL_HIST_NOT_CHANGE (65536) for any of these parameters mean that values corresponding to these parameters will not be updated.

  • AUTOEXP_ON - when set to 1 enables automatic exposure control, when 0 - disables it
  • AUTOEXP_RWIDTH, AUTOEXP_RHEIGHT - relative width and height (in %) of the auto-exposure window width and height. It is used for histogram calculation in the FPGA and so controls not only auto-exposure, but also white balance and manual histogram processing
  • AUTOEXP_RLEFT, AUTOEXP_RTOP - position of the auto-exposure window center, in %, counted from the left and top respectively
  • AUTOEXP_EXP_MAX - maximal exposure time (in 1/10000 of a second) allowed for the auto-exposure algorithm. It is recommended to set it equal to the frame period when video is streamed or recorded, otherwise in the low-light conditions the frame rate may drop to accommodate longer exposure times (frame period may never be shorter than the exposure time)
  • AUTOEXP_OVEREXP_MAX (old algorithm) - amount of pixels (fraction of all the pixels in the auto-exposure window) that are allowed to be overexposed, measured in 1/100 or a percent. This parameter is used when both AUTOEXP_S_PERCENT and AUTOEXP_S_INDEX (see below) are 0. This algorithm can be slow to handle over-exposed images and is not recommended.
  • AUTOEXP_S_PERCENT - amount of pixels that are controlled to have value below the specified AUTOEXP_S_INDEX (below), measured in 1/100 of a percent. Example: AUTOEXP_S_PERCENT=9400 means that 94% of pixels should have value lower than AUTOEXP_S_INDEX
  • AUTOEXP_S_INDEX - threshold for the pixel values (on a 0..255 8-bit scale) used together with AUTOEXP_S_PERCENT
  • AUTOEXP_SKIP_PMIN - minimal calculated exposure correction that will be applied (in 1/100 of a %). Value of 500 (default) means that exposure value will not be modified if the calculated value differs less than by 5% from the current one
  • AUTOEXP_SKIP_T minimal absolute exposure correction (in exposure steps, currently 1 step is 1/10000 sec), default value = 2
  • AUTOEXP_SKIP_PMAX - maximal allowed exposure correction that algorithm is allowed to apply (in 1/200 of a %, default is 5000 - 50%). If calculated exposure correction exceeds this value, it will not be applied immediately, the algorithm will wait for the next frame to make sure it was not an instant flash (i.e from some camera)
  • AEXPWND_WIDTH, AEXPWND_HEIGHT, AEXPWND_TOP, AEXPWND_LEFT (when written to) behave similarly to AEXPWND_RWIDTH, AEXPWND_RHEIGHT, RAEXPWND_TOP and AEXPWND_RLEFT, but come into effect immediately, not waiting for the elphel_autoexposure_set() call. Writing to AEXPWND_RWIDTH, AEXPWND_RHEIGHT, RAEXPWND_TOP and AEXPWND_RLEFT and calling elphel_autoexposure_set() updates the AEXPWND_WIDTH, AEXPWND_HEIGHT, AEXPWND_TOP, AEXPWND_LEFT

Creating custom PHP extensions for Elphel cameras

The followings steps are needed to create a custom PHP extension to run in Elphel product:

  • Create subdirectory of this (ext) directory for the new extension
  • Create files in this new directory - at least
    • config.m4
    • <your name>.h
    • <yourname>.c

You may use ext_skel script to automate this process or follow a nice tutorial by Sara Goleman Extension Writing Part I: Introduction to PHP and Zend . You may also like to read her book, "Extending and Embedding PHP".


PHP_ARG_ENABLE(hello, whether to enable Hello World support, [ --enable-hello   Enable Hello World support])
if test "$PHP_HELLO" = "yes"; then
 AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
 PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)


#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry


#include "config.h"
#include "php.h"
#include "php_hello.h"
static function_entry hello_functions[] = {
   PHP_FE(hello_world, NULL)
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
#if ZEND_MODULE_API_NO >= 20010901
   RETURN_STRING("Hello World", 1);

With the 3 files created you need to cd to the extension directory and run


That will generate support files (running phpize script) so you'll just need to run

make install

to have the new extension (as a dynamic library) installed in the target directory tree and included in the camera flash image.

You will need to add "extension=<your_extension_name>" to the php.ini file to be activated.

When troubleshooting extensions you may also want to try your scripts from the command line in the camera - in that case the stdout will be visible and PHP+your module will be able to complain about Segmentation faults (when invoked from the browser it you will get Error 500 at best if that happens). And you'll have to manually restart web server + PHP after module changes (remember - Fast CGI does not kill PHP after end of HTTP request?)

Known problems

Function fread in PHP always tries to read 8192 bytes even if the length parameter is <8192, that causes problems when read from devices (i.e. i2c) being too slow or causing side effects. It is considered feature, not a bug.

A quick fix will be to change the driver to always read minimal (1/2/3/4 bytes depending on the nature of the data) if the count passed to device read function is 8192.