Skip to content
Beat Küng edited this page Jul 16, 2012 · 5 revisions

Table of Contents

Note

This tutorial is incompatible with the new SDK from June 2009 and is therefore no longer maintained. Please use the Oscar Software Framework Manual and the templates (basic-template or app-template) instead.

Introduction

This tutorial should serve as a fast and easy entry point for LeanXcam application development. Information concerning the developement environment can be found in the Users Guide. The tutorial is based on the example projects and it is therefore recommended to get the latest source code Downloads.

This tutorial covers the following example applications:

  • Hello World :: Basic image capturing.
  • Bitmap :: Read and write bitmap files.
  • Camera :: Advanced capturing using multiple frame buffers.
  • Dma :: Dma transfers.
  • Support :: Watchdog timer and cycle count.
  • Config :: Read/Write your own config files.
  • Alarm :: Building a very basic alarm system.
All the corresponding files can be found in the examples/tutorial folder. There is also a makefile which allows to compile everything easily by issuing the command:
 $ make all

Do not forget to compile the OSCar software framework beforehand.

If you do not want to build all examples, but only a specific one for a specific platform (host pc / LeanXcam target) you can use one of the following commands:

 $ make <example name>_target

or in case of compilation for the host pc

 $ make <example name>_host

The target executables are automatically copied to the /tftpboot folder for easy deployement to the target device (more details on how to copy software to the target device can be found in Users Guide Board connectivity). The host executables reside in the source directory and can be started through the command

 $ ./<example name>

Hello World

This application should serve as a very fast success, touch, and feel example. For the sake of simplicity error checking is ommited. The complete source code can be found in the file hello-world.c. The application basically configures the camera takes a picture and writes it to a file.

Lets dive into the code.

 #if defined(OSC_HOST)
 	/*! @brief Handle to file name reader for camera images on the host. */
 	void *hFileNameReader;
 #endif
 
 	static uint8 frameBuffer[OSC_CAM_MAX_IMAGE_WIDTH * OSC_CAM_MAX_IMAGE_HEIGHT];
 	static uint8 colorPic[3 * OSC_CAM_MAX_IMAGE_WIDTH * OSC_CAM_MAX_IMAGE_HEIGHT];
 
 	uint16 i;
 	uint8 * rawPic = NULL;
 	struct OSC_PICTURE pic;
 	enum EnBayerOrder enBayerOrder;
 	
 	int32 opt_shutterWidth = 50000;
 	bool opt_debayer = false;

Ok... first we need some variables to handle the framework, modules and pictures. Because we do not have a camera on the host pc, we need an additional module to read a picture from a file instead of capturing it from the camera. Hence the

 #if defined(OSC_HOST)

preprocessor directive. The frame buffer and coloured picture arrays are declared static because the stack size is limited to 1 MB and hence does not provide enough space. Notice the three times larger size of the coloured picture array due to the three colour channels (BGR).

Next we analyze the input arguments and set the corresponding parameters.

 for (i = 1; i < argc; i += 1)
 {
 	if (strcmp(argv[i], "-d") == 0)
 	{
 		opt_debayer = true;
 	}
 	else if (strcmp(argv[i], "-s") == 0)
 	{
 		i += 1;
 		if (i >= argc)
 		{
 			printf("Error: -s needs an argument.\n");
 			return 1;
 		}
 		opt_shutterWidth = atoi(argv[i]);
 	}
 	else if (strcmp(argv[i], "-h") == 0)
 	{
 		printf("Usage: hello-world [ -h ] [ -d ] [ -s <shutter-width> ]\n");
 		printf("    -h: Prints this help.");
 		printf("    -d: Debayers the image.");
 		printf("    -s <shutter-width>: Sets the shutter width in us.");
 	}
 	else
 	{
 		printf("Error: Unknown option: %s", argv[i]);
 		return 1;
 	}
 }

Now it is time to initialize the framework and all required modules.

 	/* Load modules */
 	OscBmpCreate(hFramework);
 	OscCamCreate(hFramework);
 	OscVisCreate(hFramework);
 #if defined(OSC_HOST)
 	OscFrdCreate(hFramework);
 #endif
 
 #if defined(OSC_TARGET)
 	OscGpioCreate(hFramework);
 #endif

Again, the file reader module is only required in case of host compilation and the GPIO module (required to trigger the image capturing) only if compiled for the LeanXcam target architecture. The next step covers camera and file reader module configuration.

 #if defined(OSC_HOST)
 	OscFrdCreateConstantReader(&hFileNameReader, "imgCapture.bmp");
 	OscCamSetFileNameReader(hFileNameReader);
 #endif

For the sake of simplicity and because we only read/capture one picture during the hole program execution, a constant file reader is used. Nevertheless the file reader module (frd) provides the functionality to read different files based on a file list.

 	OscCamSetShutterWidth(opt_shutterWidth);
 	OscCamSetAreaOfInterest(0,0,OSC_CAM_MAX_IMAGE_WIDTH, OSC_CAM_MAX_IMAGE_HEIGHT);
 	OscCamSetFrameBuffer(0, OSC_CAM_MAX_IMAGE_WIDTH*OSC_CAM_MAX_IMAGE_HEIGHT, frameBuffer, TRUE);

The above code first sets the desired shutter width, then selects the maximum available capture area and binds the previously declared frame buffer to the camera module (the first parameter 0 is the frame buffer id).

Now the system is ready to capture a picture from the camera or from the specified file in case of host compilation. The capturing process basically consists of the following steps: setup with the OscCamSetupCapture function and provide a vaild frame buffer id (in this case 0); trigger the capturing either trough a external GPIO input or an internal OscGpioTriggerImage call. After these steps the image is being captured in the background. Now we can either spend the time doing something else until the capturing is done or we just perform a blocking read operation as shown here (we have nothing else to to right now). Because the camera sensor activates the new settings (shutter width, ...) after the next image has been taken, we need to capture a dummy frame. This ensures that the second capture operation is perform with the right settings. This is a somehow ugly hack, but this behaviour can usualy be negelcted in a stream oriented capturing porcess because it only affects the first of lets say hundreds or even more pictures.

 	OscCamSetupCapture(0);
 	OscGpioTriggerImage();
 #endif
 	OscCamReadPicture(0, (void *) &rawPic, 0, 0);

Now we would like to see what we captured and therefore write the picture to a file in the http daemons publishing folder. If we want a coloured picture (enabled through the "-d" option) an additional step called debayering (reversing the effect of the installed bayer filter) is required. Luckily, the cam and vis modules provide us with all the required tools.

 	if (opt_debayer)
 	{
 		OscCamGetBayerOrder(&enBayerOrder, 0, 0);
 		OscVisDebayer(frameBuffer, OSC_CAM_MAX_IMAGE_WIDTH, OSC_CAM_MAX_IMAGE_HEIGHT, enBayerOrder, colorPic);
 		pic.type = OSC_PICTURE_BGR_24;
 		pic.data = colorPic;
 	}
 	else
 	{
 		pic.type = OSC_PICTURE_GREYSCALE;
 		pic.data = rawPic;
 	}
 
 	OscBmpWrite(&pic, "/home/httpd/hello-world.bmp~");
 	rename("/home/httpd/hello-world.bmp~", "/home/httpd/hello-world.bmp");

We are almost done now. There is just one task left: unloading all modules and the framework.

 #if defined(OSC_TARGET)
 	OscGpioDestroy(hFramework);
 #endif
 
 	/* Destroy framework */
 	OscDestroy(hFramework);
 
 	return 0;
 }

We can now look at the picture with a web-browser ("leanXcamIP" needs to be changed to the actual IP of your camera):

 http://leanXcamIP/hello-world.bmp

Note: If the option -s (shutter width) does not work as expected, AEC or AGC might be active. See Sensor Registers to figure out how to disable these features.

Bitmap

This simple application covers the use and functionality of the bitmap module, which basically sums up to reading and writing pictures to/from bitmap files. The complete source code can be found in the file bmp.c.

Ok. First we need a main function and a handle for the oscar framework.

 int main(const int argc, const char * argv[])
 {
 	void *hFramework;

Next we need a picture data structure and we have to ensure that it is properly initialized.

 	struct OSC_PICTURE pic;
 	memset(&pic, 0, sizeof(struct OSC_PICTURE));

We can now create the framework, load the bitmap module, read a picture from a file and store it in the new picture data structure.

 	OscCreate(&hFramework);
 	OscBmpCreate(hFramework);
 	OscBmpRead(&pic, "imgCapture.bmp");

The data member of the OSC_PICTURE structure gives us now access to the raw pixels of the image. In case of a greyscale picture, indicated by the type member, each pixel is represented by one byte. In case of a coloured picture each pixel is represented by three bytes corresponding to the colors blue, green and red.

Now here is the place where you probably want to put your image processing code.

After processing the data you would probably like to save your modified picture as a new bitmap file. Hence we need to set the dimensions and type of our "new" picture.

 	pic.width = OSC_CAM_MAX_IMAGE_WIDTH;
 	pic.height = OSC_CAM_MAX_IMAGE_HEIGHT;
 	pic.type = OSC_PICTURE_GREYSCALE;  // in case of a colored picture: OSC_PICTURE_BGR_24

A simple function call writes the data associated with the picture structure to a file.

 	OscBmpWrite(&pic, "modified.bmp");

Finally we unload the modules and framework

 	OscBmpDestroy(hFramework);
 	OscDestroy(hFramework);
 	return 0;
 }

Camera

This example shows how to use the camera and its features. The complete source code can be found in the file cam.c.

First, as always, main function and framework handle.

 int main(const int argc, const char * argv[])
 {
 	void *hFramework;

As we want to compile and run our example on the target and host platform we need a file reader handle.

 #if defined(OSC_HOST)
 	/* Handle to file name reader, if compiled for host platform. */
 	void *hFileNameReader;
 #endif

Some variables for the camera parameters...

 	uint32 shutterWidth;
 	uint16 x, y, width, height;
 	uint16 blackOffset;
 	uint8 bufferIDs[2];

...Frame buffers...



...picture and picture data pointer...

 	struct OSC_PICTURE p;
 	void *pic;

...error handling variable.

 	OSC_ERR err = SUCCESS;

Create the framework and load the required modules...

 	OscCamCreate(hFramework);
 
 #if defined(OSC_TARGET)
 	OscGpioCreate(hFramework);
 #endif	
 
 #if defined(OSC_HOST)
 	OscFrdCreate(hFramework);
 	OscFrdCreateConstantReader(&hFileNameReader, "imgCapture.bmp");
 #endif

Before we capture a picure we should set its parameters (width, height and type).

 	p.width = OSC_CAM_MAX_IMAGE_WIDTH;
 	p.height = OSC_CAM_MAX_IMAGE_HEIGHT;
 	p.type = OSC_PICTURE_GREYSCALE;

Some camera module functions allow us to read the current camera settings (shutter width, area of interest, ...). For demonstration purposes we simply read and print them to the console. The shutter width is a value in microseconds. The x and y variables contain, after the function call, the coordinates of the upper left corner of the area of interest. The black level offset is used to control the black-level response. You might want to adjust this together with the sensor calibration to get an undistorted black image when the lense is covered. The black level offset boosts the sensor response by N grey levels.

 	OscCamGetShutterWidth(&shutterWidth);
 	OscCamGetAreaOfInterest(&x, &y, &width, &height);
 	OscCamGetBlackLevelOffset(&blackOffset);
 	printf("ShutterWidth=%lu\n",shutterWidth);
 	printf("AreaOfInterest: x=%u y=%u width=%u height=%u\n", x, y, width, height);
 	printf("BlackLevelOffset=%u\n",blackOffset);

If we realize that the current settings do not fit our needs, we can set new values using the OscCamSet functions.

 	shutterWidth = 50000; /* set shutter to 50ms */
 	OscCamSetShutterWidth(shutterWidth);
 	OscCamSetAreaOfInterest(0,0,OSC_CAM_MAX_IMAGE_WIDTH, OSC_CAM_MAX_IMAGE_HEIGHT);
 	OscCamSetBlackLevelOffset(blackOffset);
 	OscCamSetupPerspective(OSC_CAM_PERSPECTIVE_VERTICAL_MIRROR);

The last function call enables us to adapt the camera perspective. Available options are: OSC_CAM_PERSPECTIVE_DEFAULT, OSC_CAM_PERSPECTIVE_HORIZONTAL_MIRROR, OSC_CAM_PERSPECTIVE_VERTICAL_MIRROR, OSC_CAM_PERSPECTIVE_180DEG_ROTATE.

In case of host platform compilation we need to setup a file reader.

 #if defined(OSC_HOST)
 	OscCamSetFileNameReader(hFileNameReader);
 #endif

Next we setup the camera to use two buffers. The advantage of a double buffer system is that we can process the data from the first buffer and simultanuously capture a new picture to the second buffer. When processing and capturing is done we simply flip the buffers and process/capture the next frame. To enable multi buffering we first need to call the function OscCamSetFrameBuffer for each buffer we want to use (the first argument is the buffer id). Then we need to tell the system through the function call OscCamCreateMultiBuffer how many buffers to use. This function takes the number of buffers and an array with all buffer id's as arguments.

 	OscCamSetFrameBuffer(0, OSC_CAM_MAX_IMAGE_WIDTH * OSC_CAM_MAX_IMAGE_HEIGHT, frameBuffer0, TRUE);
 	OscCamSetFrameBuffer(1, OSC_CAM_MAX_IMAGE_WIDTH * OSC_CAM_MAX_IMAGE_HEIGHT, frameBuffer1, TRUE);
 	bufferIDs[0] = 0;
 	bufferIDs[1] = 1;
 	OscCamCreateMultiBuffer(2, bufferIDs);

The last step of the setup and initialization procedure is the capturing. The system flips the buffers by itself and hence we do not need to provide a specific buffer id to the OscCamSetupCapture function, but simply pass the OSC_CAM_MULTI_BUFFER constant. We also use this last setup step to introduce error handling. Almost all framework functions return a OSC_ERR type error message which can and should be used to handle errors. For the sake of simplicity most of the error handling is omitted in this tutorial.

 	err = OscCamSetupCapture(OSC_CAM_MULTI_BUFFER);
 	if(err != SUCCESS){
 		printf("%s: Unable to setup initial capture (%d)!\n", __func__, err);
 		return err;
 	}

Now, we are finally done with setup and initialization and can trigger an image. This step is only required on the target platform. By default the system is configured to use internal triggering. External triggering through one of the GPIO's can be configured with the function OscGpioConfigImageTrigger.

 #if defined(OSC_TARGET)
 	OscGpioTriggerImage();
 #endif

Until the new picture is sucessfully captured we can either do someting else or just wait with a blocking read call. The first parameter of OscCamReadPicture is the buffer id (in our case indicating multi buffer), the second is a data pointer which points afterwards to the raw data of the new picture, the third parameter defines the max age of the picture that we accept (zero indicates no max. age) and the fourth parameter defines a timeout (zero means no timeout and hence blocking read).

 	OscCamReadPicture(OSC_CAM_MULTI_BUFFER, &pic, 0, 0);

Before we process the first picture we already start capturing the next one.

 	/* Process first picture parallel to capturing second picture */
 	/* ---------------------------------------------------------- */
 	
 	/* Read second picture */
 	OscCamReadPicture(OSC_CAM_MULTI_BUFFER, &pic, 0, 0);
 	
 	/* Process second picture */
 	/* ---------------------- */

Finally we need to unload all modules and the framework.

 #if defined(OSC_HOST)
 	OscFrdDestroy(hFramework);
 #endif
 
 	OscDestroy(hFramework);
 	return 0;
 }

This example did not cover all the functionality and correct error handling of the camera module. More details can be found in the doxygen based API documentation.

Support

This example application demonstrates how to use the integrated watchdog timer and cycle count functions. The complete source code can be found in the file sup.c.

First, as always, framework handle and variable declaration...

 int main(const int argc, const char * argv[])
 {
 	void *hFramework;
 	float time;
 	uint32 cyclesStart, cyclesEnd;

Create the framework and load the module...

 	OscCreate(&hFramework);
 	OscSupCreate(hFramework);

Now we initialize the watchdog with the default timeout of 1 minute.

 	OscSupWdtInit();

Ok this is the place to put some significant code. Because we have nothing important to do in this example, we just print the remaining time until reboot.

 	cyclesStart = OscSupCycGet();
 	time = 60.0;
 	do {
 		sleep(1);
 		cyclesEnd = OscSupCycGet();
 		time -= ((float)OscSupCycToMicroSecs(cyclesEnd - cyclesStart) / 1000000);
 		printf("System reboot in: %f seconds\n",time);
 		cyclesStart = cyclesEnd;
 	} while (time > 1);

Ok we don't actually want the system to reboot and kill our very important live-sustaining application, which is why we disarm the reboot in the last second...

 	OscSupWdtKeepAlive();
 	printf("Reboot avoided!\n");

Again this is the place to put some serious code. because there is still no important work for this application we just waist some time again and print the remaining time until reboot...



...disable the watchdog and close the application.

 	OscSupDestroy(hFramework);
 	OscDestroy(hFramework);
 	return 0;
 }

Config

This example application shows how to read and write configuration files using the support module. The complete source code can be found in the file cfg.c.

A config file has the following basic structure:

 SECTION
 NAME: Sample Section

This example file has two so called sections. A "global" section and a section called "SECTION". The string before the colon is the tag and the string after the colon the corresponding value.

As always the program starts with a framework handle and variable declaration...

 int main(const int argc, const char * argv[])
 {
 	void *hFramework;
 	struct CFG_KEY key;
 	struct CFG_VAL_STR val;
 	int16 number1;
 	int32 number2;
 	char strNumber[10];
 	CFG_FILE_CONTENT_HANDLE hCfg;
 	const char fileName[] = "config.txt";

The CFG_KEY and CFG_VAL_STR structs are used to access sections and tags of the config file.

Next we create the framework and load the module...

 	OscCreate(&hFramework);
 	OscCfgCreate(hFramework);

Register the config file

 	OscCfgRegisterFile(&hCfg, fileName, 1024);

Now we would like to read the content of the global section and hence set the section property of the key struct to "NULL".

 	key.strSection = NULL;

Next we want to read the values of the three tags "NAME", "16BIT", "32BIT" and print them on the console. The value corresponding to the "NAME" tag is of type string and hence we use the function OscCfgGetStr(). For the 16 bit value OscCfgGetInt() is the right choice and in case of the 32 bit value OscCfgGetInt32().

 	key.strTag = "16BIT";
 	OscCfgGetInt(hCfg, &key, &number1);
 	printf("16BIT: %d\n",number1);
 
 	key.strTag = "32BIT";
 	OscCfgGetInt32(hCfg, &key, &number2);
 	printf("32Bit: %ld\n",number2);

Now we can read, but we also want to write stuff to the file. The next lines create a new section and two tags.

 	key.strTag = "NEW32BIT";
 	sprintf(strNumber,"%ld",-number2);
 	OscCfgSetStr(hCfg, &key, strNumber);

The numerical 32 bit value needs to be converted to a string using sprintf.

Unload modules and destroy framework...

 	OscCfgFlushContent(hCfg);
 	OscCfgDestroy(hFramework);
 	OscDestroy(hFramework);
 	return 0;
 }

In order for this application to work, it is required that the config.txt file resides in the same folder as the executable.

Alarm

The alarm.c example application implements a very basic alarm system and demonstrates how easy it is to build a "smart" camera based on the LeanXcam system. The basic algorithm behind this application is simple but effective. The idea is to fire an alarm if something in front of the camera changes, but slow light changes (day-night-shifts) should not fire an alarm. Therefore the system analyzes a certain number of previous pictures and compares them to the newest one. Instead of temporarily storing all the pixels of each history picture and hence wasting a lot of memory with unnecessary information, each picture is processed and the mean is stored as decision parameter. If the mean of the newest picture exceeds the mean of all history pictures by a certain threshold an alarm is fired. Fireing an alarm means that a GPIO pin is activated. For illustration purposes two LED's can be connected to the GPIO's 1 and 2. This example is only supported on the target platform since it does not make sense to wait for something to change in a fixed bitmap file.

 #include "inc/oscar.h"
 #include <stdio.h>
 #include <unistd.h>

First some definitions to easily configure the most important alarm parameters

 #define HISTORY_LENGTH 20  /* Number of previous mean values we take into consideration. */
 #define IMAGE_WIDTH 752    /* Image dimensions */
 #define IMAGE_HEIGHT 480
 #define THRESHOLD 2        /* Alarm threshold */
 

To simplify the module handling we use the OSC_DEPENDENCY struct and define all modules we would like to load/unload.

 /*! Global variables. */
 int led = 0;
 

As always... main procedure and some variables and buffers.



Create framework...



This time we can easily load all modules with one function call and our dependency struct...

 	err = OscLoadDependencies(hFramework, deps, sizeof(deps)/sizeof(struct OSC_DEPENDENCY));
 	if (err != SUCCESS) {
 		fprintf(stderr, "%s: ERROR: Unable to load dependencies! (%d)\n", __func__, err);
 		return err;
 	}

GPIO configuration... We choose GPIO_OUT1 to indicate the survaillance mode and GPIO_OUT2 to indicate an intruder.

  	err = OscGpioSetupPolarity(GPIO_OUT1, FALSE);
  	if (err != SUCCESS) {
 	  fprintf(stderr, "%s: ERROR: Unable to set GPIO! (%d)\n", __func__, err);
 	  return err;
 	}
 	err = OscGpioSetupPolarity(GPIO_OUT2, FALSE);
 	if (err != SUCCESS) {
 	  fprintf(stderr, "%s: ERROR: Unable to set GPIO! (%d)\n", __func__, err);
 	  return err;
 	}
 	OscGpioWrite(GPIO_OUT1, FALSE);
 	OscGpioWrite(GPIO_OUT2, FALSE);

Camera and picture setup. More details can be found in the camera example above or the doxygen based API documentation.

 	/* Configure camera */
 	OscCamPresetRegs();
 	OscCamSetAreaOfInterest(0,0,IMAGE_WIDTH,IMAGE_HEIGHT);
 	OscCamSetFrameBuffer(0, IMAGE_WIDTH * IMAGE_HEIGHT, frameBuffer, TRUE);
 	OscCamSetShutterWidth(50000); /* 50 ms shutter */
 

Now we need to fill our mean history buffer with real world data or otherwise the system would raise an alarm after entering surveillance mode because every picture would exceed the threshold. Hence we capture HISTORY_LENGTH pictures, calculate their mean, and store the mean in the history buffer (the actual mean calculation is done with the help of the mean() function which will be discussed later).

 		meanBuffer[i] = mean(&pic);
 	}
 

Now we are ready to enter the surveillance mode, which basically consists of an infinite loop.



First we indicate our surveillance activity by toggling the LED on GPIO 1.



Next we capture a new picture...



Calculate it's mean...

 	    m = mean(&pic);

Calculate the mean of our history buffer...



Check if the threshold is reached and therefore an alarm should be raised...

 	        /* Indicate detected intruder with LED */
 	        err = OscGpioWrite(GPIO_OUT2, TRUE);
 		if (err != SUCCESS) {
 
 		  fprintf(stderr, "%s: ERROR: GPIO write error! (%d)\n", __func__, err);
 		  return err;
 		}
 		err = OscGpioWrite(GPIO_OUT1, FALSE);
 		if (err != SUCCESS) {
 		  fprintf(stderr, "%s: ERROR: GPIO write error! (%d)\n", __func__, err);
 		  return err;
 		}
 		led = 0;
 

Now we would also like to save the picture of the intruder to a bitmap file...



This is the time and place to execute your secret threat elimination code (we just wait for two seconds and hope everything is fine afterwards).



Ok. The threat seems to be gone, we can now stop the alarm signal and reinitialize the history buffer.

 		/* Re-Initialize mean buffer */
 		for (i = 0; i < HISTORY_LENGTH; i++) {
 		  err = OscCamSetupCapture(0);
 		  if (err != SUCCESS) {
 			fprintf(stderr, "%s: ERROR: Unable setup capture! (%d)\n", __func__, err);
 			return err;
 		  }
 		  err = OscGpioTriggerImage();
 		  if (err != SUCCESS) {
 			fprintf(stderr, "%s: ERROR: Unable to trigger! (%d)\n", __func__, err);
 			return err;
 		  }
 		  err = OscCamReadPicture(0, &pic.data, 0, 0);
 		  if (err != SUCCESS) {
 			fprintf(stderr, "%s: ERROR: Unable read picture! (%d)\n", __func__, err);
 			return err;
 		  }
 
 		  meanBuffer[i] = mean(&pic);
 		}
 
 		/* Reset buffer index */
 		bufferIndex = 0;
 	    }else{

If the threshold is not reached we simply add the new mean to our history buffer and jump to the top of our surveillance loop.

 		/* Update buffer index */
 		bufferIndex = bufferIndex % HISTORY_LENGTH;
 	    }
 
 	    /* Reset mean history */
 	    n = 0;
 	}
 
 
 	/* Destroy modules */
 	OscUnloadDependencies(hFramework, deps, sizeof(deps)/sizeof(struct OSC_DEPENDENCY));
 
 	/* Destroy framework */
 	OscDestroy(hFramework);
 
 	return 0;
 }

The mean calculation is rather simple, we add up the raw pixel data and divide it by the number of pixels.

 int mean(struct OSC_PICTURE *pic)
 {
 	uint16 i,j;
 	uint32 sum = 0;
 	uint8 *p;
 	p = (uint8*)pic->data;
 	for(i = 0; i < pic->height; i++){
 		for(j = 0; j < pic->width; j++){
 			sum += p[i*pic->width + j];
 		}
 	}
 	sum = sum / (pic->width * pic->height);
 	return sum;
 }

LED toggle function.

     return;
 }
Clone this wiki locally