Sample code of section 10 in Nov 2014 issue of the Interface Magazine, published by CQ publishing in Japan. CQ出版社インターフェース誌 2014年11月号10章に掲載のサンプルコードです. LPC1768にトラ技OV7670モジュールを接続したうえで,省メモリ画像処理(色による簡易顔検出)を行う事例です.画像フレームバッファを確保できないとき,ストリーム処理で画像を加工するのがテーマです.このコードのうちカメラ制御部には,Sadaei Osakabe氏のコードを流用させていただいています.

Dependencies:   TextLCD mbed

main.cpp

Committer:
smorioka
Date:
2014-09-24
Revision:
0:d2c78848026d

File content as of revision 0:d2c78848026d:

/*
 * Copyright (c) 2014, Sumio Morioka
 * All rights reserved.
 *
 * This source code was originally written by Dr.Sumio Morioka for use in the Nov 2014 issue of 
 * "the Interface magazine", published by CQ publishing Co.Ltd in Japan (http://www.cqpub.co.jp).
 * The author has no responsibility on any results caused by using this code.
 *
 * - Distribution date of this code: Sep 24, 2014
 * - Author: Dr.Sumio Morioka (http://www002.upp.so-net.ne.jp/morioka)
 *
 *
 * IMPORTANT NOTICE:
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
#include "mbed.h"
#include "TextLCD.h"
#include "LocalFileSystem.h"

#include "ov7670.h"

TextLCD lcd(p24, p26, p27, p28, p29, p30);
DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4);
PwmOut sv0(p21), sv1(p22), sv2(p23);

LocalFileSystem local("webfs");

OV7670 camera(
    p9,p10,			// SDA,SCL(I2C / SCCB)
    p5,p6,p7,		// VSYNC,HREF,WEN(FIFO)
    p20,p19,p18,p17,p16,p15,p14,p13,	// D7-D0
    p8,p11,p12);	// RRST,OE,RCLK

Timer tmr;

//#define QQVGA
#define QVGA
//#define VGA34

#ifdef QQVGA
#define SIZEX 160
#define SIZEY 120
#endif

#ifdef QVGA
#define SIZEX 320
#define SIZEY 240
#endif

#ifdef VGA34
#define SIZEX 480
#define SIZEY 360
#endif


#define	SHUTTER_THRESHOLD	3500	// # of skin color pixels


/////////////////////////////////////////////////////////////////////////////////

void rgb2hsv(
	unsigned char	rval,
	unsigned char	gval,
	unsigned char	bval,

	unsigned char	*hval,
	unsigned char	*sval,
	unsigned char	*vval
)
{
	unsigned char	max, min;
	int				ret;

	max	= (rval >= gval) ? rval : gval;
	max	= (bval >= max)  ? bval : max;

	min	= (rval <= gval) ? rval : gval;
	min	= (bval <= min)  ? bval : min;

	///////////////////////////////////////////
	// compute H
	///////////////////////////////////////////
	if (max == min)
		ret	= 0;	// (undef)
	else if (rval >= gval && rval >= bval)	// max: R
		ret	= ((gval - bval) * 60) / (max - min);
	else if (gval >= rval && gval >= bval)	// max: G
		ret	= ((bval - rval) * 60) / (max - min) + 120;
	else	// max: B
		ret	= ((rval - gval) * 60) / (max - min) + 240;

	ret	%= 360;
	if (ret < 0)
		ret	+= 360;

	*hval	= (ret * 255) / 360;

	///////////////////////////////////////////
	// compute S
	///////////////////////////////////////////
	if (max == 0)
		ret	= 0;	// (undef)
	else
		ret	= ((max - min) * 255) / max;

	*sval	= ret;

	///////////////////////////////////////////
	// compute V
	///////////////////////////////////////////
	*vval	= max;
}

/////////////////////////////////////////////////////////////////////////////////

unsigned char judge_skin_color(
	unsigned char	hval,
	unsigned char	sval,
	unsigned char	vval
)
{
	if (hval <= 70 && sval >= 25 && sval <= 70 && vval >= 180 && vval <= 250)	// <rgain,ggain,bgain> == <0x48,0x40,0x60>
		return (255);
	else
		return (0);
}

/////////////////////////////////////////////////////////////////////////////////

typedef	unsigned char (*kernel_func)(unsigned char *linebuf, int x, int y, int line_size, int kernel_size, void *param);	// kernel operation function

typedef struct _stream_filter
{
	// input pixel data
	unsigned char	d_in0;			// plane 0
	unsigned char	d_in1;			// plane 1
	unsigned char	d_in2;			// plane 2
	unsigned char	d_in_attr;		// data attribute (see the definitions below)

	// output pixel data
	unsigned char	d_out0;			// plane 0
	unsigned char	d_out1;			// plane 1
	unsigned char	d_out2;			// plane 2
	unsigned char	d_out_attr;		// data attribute (see the definitions below)

	// output data
	char			is_3_planes;	// 0: single plane, Others: 3 planes

	////////////////////////////////////////
	// internal variables (do not touch)
	////////////////////////////////////////
	char			state;			// internal 

	int				d_in_cnt;		// # of total input
	int				d_in_x;			// x-coordinate of input position on "line_buf" (not on entire screen)
	int				d_in_y;			// y-coordinate

	int				d_out_cnt;		// # of total output
	int				d_out_x;		// x-coordinate of center of kernel on "line_buf" (not on entire screen)
	int				d_out_y;		// y-coordinate

	int				line_size;		// width
	int				kernel_size;	// kernel size (MUST be odd)

	unsigned char	*line_buf0;		// line buffer for plane 0
	unsigned char	*line_buf1;		// plane 1
	unsigned char	*line_buf2;		// plane 2

	kernel_func		func;			// pointer to a kernel operation function
	void			*func_param;	// parameter to the above function (ex. pointer to coefficient matrix etc.)
} stream_filter;


#define	ATTR_1ST		0		// 1st pixel data
#define	ATTR_VALID		1		// valid pixel data (not 1st nor last)
#define	ATTR_LAST		2		// last pixel data
#define	ATTR_INVALID	3		// invalid pixel data

#define	STATE_PROCESS	0		// do not touch
#define	STATE_FLUSH		1		// do not touch

#ifdef DEBUG
FILE *fp_his;
#endif	// DEBUG


///////////////////--> kernel func
unsigned char comb_filter(unsigned char *linebuf, int x, int y, int line_size, int kernel_size, void *matrix_void)
{
	float	acc		= 0.0f;
	int		mat_idx	= 0;

	////////////////////////////////////////////// ---> customize here
	float	*matrix	= (float *)matrix_void;
	////////////////////////////////////////////// <--- customize here

#ifdef DEBUG
	fprintf(fp_his, "\tpixel: ");
#endif

	// scan kernel
	for (int ky = y - ((kernel_size - 1) / 2); ky <= y + (kernel_size - 1) / 2; ky++) {
		int		ly;
		// (rounding linebuf)
		if (ky < 0)
			ly	= ky + (kernel_size + 1);
		else if (ky >= kernel_size + 1)
			ly	= ky - (kernel_size + 1);
		else
			ly	= ky;

		for (int kx = x - ((kernel_size - 1) / 2); kx <= x + (kernel_size - 1) / 2; kx++, mat_idx++) {
			if (kx >= 0 && kx < line_size) {
				float	pixel;
				pixel	= (float)(*(linebuf + (ly * line_size) + kx));

				////////////////////////////////////////////// ---> customize here
#ifdef DEBUG
				fprintf(fp_his, "%02x ", *(linebuf + (ly * line_size) + kx));
#endif
				acc		+= pixel * (*(matrix + mat_idx));
				////////////////////////////////////////////// <--- customize here
			}
		}

#ifdef DEBUG
		fprintf(fp_his, ", ");
#endif
	}

#ifdef DEBUG
	fprintf(fp_his, "(ret %d)\n", (unsigned char)(acc + 0.5f));
#endif

	return ((unsigned char)(acc + 0.5f));
}
///////////////////<-- kernel func


void dump_stream_filter(FILE *fp, stream_filter *flt)	// for debug; dump contents of the structure data
{
	fprintf(fp, "d_in %02x ", flt->d_in0);
	if (flt->d_in_attr == ATTR_1ST)				fprintf(fp, "ATTR_1ST");
	else if (flt->d_in_attr == ATTR_VALID)		fprintf(fp, "ATTR_VALID");
	else if (flt->d_in_attr == ATTR_LAST)		fprintf(fp, "ATTR_LAST");
	else if (flt->d_in_attr == ATTR_INVALID)	fprintf(fp, "ATTR_INVALID");
	else										fprintf(fp, "illegal attribute");
	fprintf(fp, "\tcnt %d, x %d, y %d\n", flt->d_in_cnt, flt->d_in_x, flt->d_in_y);

	fprintf(fp, "d_out %02x ", flt->d_out0);
	if (flt->d_out_attr == ATTR_1ST)			fprintf(fp, "ATTR_1ST");
	else if (flt->d_out_attr == ATTR_VALID)		fprintf(fp, "ATTR_VALID");
	else if (flt->d_out_attr == ATTR_LAST)		fprintf(fp, "ATTR_LAST");
	else if (flt->d_out_attr == ATTR_INVALID)	fprintf(fp, "ATTR_INVALID");
	else										fprintf(fp, "illegal attribute");
	fprintf(fp, "\tcnt %d, x %d, y %d\n", flt->d_out_cnt, flt->d_out_x, flt->d_out_y);

	if (flt->state == STATE_PROCESS)			fprintf(fp, "state: STATE_PROCESS\n");
	else										fprintf(fp, "state: STATE_FLUSH\n");

	fprintf(fp, "\n");
	for (int y = 0; y < flt->kernel_size + 1; y++) {
		for (int x = 0; x < flt->line_size; x++) {
			fprintf(fp, "%02x ", *(flt->line_buf0 + (y * flt->line_size) + x) );
		}
		fprintf(fp, "\n");
	}

	if (flt->is_3_planes != 0) {
		fprintf(fp, "\n");
		for (int y = 0; y < flt->kernel_size + 1; y++) {
			for (int x = 0; x < flt->line_size; x++) {
				fprintf(fp, "%02x ", *(flt->line_buf1 + (y * flt->line_size) + x) );
			}
			fprintf(fp, "\n");
		}

		fprintf(fp, "\n");
		for (int y = 0; y < flt->kernel_size + 1; y++) {
			for (int x = 0; x < flt->line_size; x++) {
				fprintf(fp, "%02x ", *(flt->line_buf1 + (y * flt->line_size) + x) );
			}
			fprintf(fp, "\n");
		}
	}

	fprintf(fp, "====================================================\n");
	fflush(fp);
}


// reset data structure for reuse
void reset_stream_filter(stream_filter *flt)
{
	if (flt == (stream_filter *)NULL)
		return;

	flt->state			= STATE_PROCESS;

	flt->d_in_cnt		= 0;								// total # of data
	flt->d_in_x			= 0;								// linebuf pointer
	flt->d_in_y			= (flt->kernel_size - 1) / 2;		// linebuf pointer

	flt->d_out_cnt		= 0;								// total # of data
	flt->d_out_x		= 0;								// linebuf pointer
	flt->d_out_y		= (flt->kernel_size - 1) / 2;		// linebuf pointer

	// zero clear buffer
	if (flt->line_buf0 != (unsigned char *)NULL) {
		for (int i = 0; i < flt->line_size * (flt->kernel_size + 1); i++) {
			*(flt->line_buf0 + i)	= 0;
		}
	}

	if (flt->line_buf1 != (unsigned char *)NULL) {
		for (int i = 0; i < flt->line_size * (flt->kernel_size + 1); i++) {
			*(flt->line_buf1 + i)	= 0;
		}
	}

	if (flt->line_buf2 != (unsigned char *)NULL) {
		for (int i = 0; i < flt->line_size * (flt->kernel_size + 1); i++) {
			*(flt->line_buf2 + i)	= 0;
		}
	}
}


// allocate and initialize data structure
stream_filter *create_stream_filter(char is_3_planes, int line_size, int kernel_size, kernel_func func, void *func_param)
{
	stream_filter	*ret;

	// allocate memory
	if ((ret = (stream_filter *)malloc(sizeof (stream_filter))) == (stream_filter *)NULL)
		return ((stream_filter *)NULL);

	if ((ret->line_buf0 = (unsigned char *)malloc(sizeof (unsigned char) * line_size * (kernel_size + 1))) == (unsigned char *)NULL) {
		free(ret);
		return ((stream_filter *)NULL);
	}

	if (is_3_planes != 0) {
		if ((ret->line_buf1 = (unsigned char *)malloc(sizeof (unsigned char) * line_size * (kernel_size + 1))) == (unsigned char *)NULL) {
			free(ret->line_buf0);
			free(ret);
			return ((stream_filter *)NULL);
		}

		if ((ret->line_buf2 = (unsigned char *)malloc(sizeof (unsigned char) * line_size * (kernel_size + 1))) == (unsigned char *)NULL) {
			free(ret->line_buf0);
			free(ret->line_buf1);
			free(ret);
			return ((stream_filter *)NULL);
		}
	}
	else {
		ret->line_buf1	= (unsigned char *)NULL;
		ret->line_buf2	= (unsigned char *)NULL;
	}

	// init internal valiables
	ret->is_3_planes	= is_3_planes;
	ret->line_size		= line_size;
	ret->kernel_size	= kernel_size;

	reset_stream_filter(ret);

	// init kernel operation func
	ret->func		= func;
	ret->func_param	= func_param;

	return (ret);
}


// process 1 pixel; Set d_in & d_in_attr before calling. The result will be set to d_out & d_out_attr.
void apply_filter(stream_filter *flt)
{
	if (flt == (stream_filter *)NULL)
		return;

	if ((flt->state == STATE_PROCESS && flt->d_in_attr != ATTR_INVALID)
			|| (flt->state == STATE_FLUSH && flt->d_in_attr == ATTR_INVALID)) {
		/////////////////////////////////////////////
		// store d_in to linebuf
		/////////////////////////////////////////////
		if (flt->d_in_attr == ATTR_INVALID) {
			flt->d_in0	= 0;
			flt->d_in1	= 0;
			flt->d_in2	= 0;
		}

		*(flt->line_buf0 + (flt->d_in_y * flt->line_size) + flt->d_in_x)	= flt->d_in0;
		if (flt->is_3_planes != 0) {
			*(flt->line_buf1 + (flt->d_in_y * flt->line_size) + flt->d_in_x)	= flt->d_in1;
			*(flt->line_buf2 + (flt->d_in_y * flt->line_size) + flt->d_in_x)	= flt->d_in2;
		}

		// update input counter
		if (flt->state == STATE_PROCESS && flt->d_in_attr != ATTR_LAST)
			(flt->d_in_cnt)++;

		// update input pointer
		(flt->d_in_x)++;
		if (flt->d_in_x >= flt->line_size) {
			flt->d_in_x	= 0;
			(flt->d_in_y)++;
			if (flt->d_in_y >= flt->kernel_size + 1) {
				flt->d_in_y	= 0;
			}
		}

		/////////////////////////////////////////////
		// calc output (NOTE: do after updating input counter)
		/////////////////////////////////////////////
		if ((flt->state == STATE_PROCESS && flt->d_in_cnt > flt->line_size * (flt->kernel_size + 1) / 2)
				|| (flt->state == STATE_FLUSH && flt->d_in_cnt >= flt->d_out_cnt)) {	// normal operation

			// generate output (call kernel_operation func)
			flt->d_out0	= (flt->func)(flt->line_buf0, flt->d_out_x, flt->d_out_y, flt->line_size, flt->kernel_size, flt->func_param);
			if (flt->is_3_planes != 0) {
				flt->d_out1	= (flt->func)(flt->line_buf1, flt->d_out_x, flt->d_out_y, flt->line_size, flt->kernel_size, flt->func_param);
				flt->d_out2	= (flt->func)(flt->line_buf2, flt->d_out_x, flt->d_out_y, flt->line_size, flt->kernel_size, flt->func_param);
			}

			if (flt->d_out_cnt == 0)
				flt->d_out_attr	= ATTR_1ST;
			else if (flt->d_out_cnt == flt->d_in_cnt)
				flt->d_out_attr	= ATTR_LAST;
			else
				flt->d_out_attr	= ATTR_VALID;

			// update output counter
			(flt->d_out_cnt)++;

			// update output pointer
			(flt->d_out_x)++;
			if (flt->d_out_x >= flt->line_size) {
				flt->d_out_x	= 0;
				(flt->d_out_y)++;
				if (flt->d_out_y >= flt->kernel_size + 1) {
					flt->d_out_y	= 0;
				}
			}
		}
		else {	// initial operation (still filling buffer) or completion of data output
			// no output
			flt->d_out0		= 0;
			flt->d_out1		= 0;
			flt->d_out2		= 0;
			flt->d_out_attr	= ATTR_INVALID;
		}

		/////////////////////////////////////////////
		// change state
		/////////////////////////////////////////////
		if (flt->state == STATE_PROCESS && flt->d_in_attr == ATTR_LAST) {
			flt->state	= STATE_FLUSH;
		}
	}
}


// release data structure
void release_stream_filter(stream_filter *flt)
{
	if (flt == (stream_filter *)NULL)
		return;
	if (flt->line_buf0 != (unsigned char *)NULL)
		free(flt->line_buf0);
	free(flt);
}


/////////////////////////////////////////////////////////////////////////////////


//void cam_cap(void);
int cam_cap(char, stream_filter *);


int memfree(void)
{
	int	ret = 1;
	while (1) {
		char	*p	= (char *)malloc(ret);
		if (p == NULL)
			break;
		free(p);
		ret++;
	}
	return (ret - 1);
}


int main() 
{
    led1 = 0;
    led2 = 0;
    led3 = 0;
    led4 = 0;

	sv0.period_us(20000);		// 20ms
	sv0.pulsewidth_us(5000);	// 5ms

	sv1.period_us(20000);		// 20ms
	sv1.pulsewidth_us(10000);	// 10ms

	sv2.period_us(20000);		// 20ms
	sv2.pulsewidth_us(15000);	// 15ms
	
	////////////////////////////////////////////////////////////////////////////
    camera.WriteReg(0x12, 0x80);			// com7; reset
    wait_ms(200);

	camera.InitDefaultReg();

    // negate vsync
    camera.WriteReg(0x15, 0x02);			// com10; negative vsync

#ifdef QQVGA
	camera.InitQQVGA();
#endif
#ifdef QVGA
	camera.InitQVGA();
#endif
#ifdef VGA34
	camera.InitVGA_3_4();
#endif

    // data format
    camera.WriteReg(0x12, 0x04 + 0);    // com7 RGB	(bit1...test pattern)
    camera.WriteReg(0x40, 0xD0);    // com15 RGB565
    camera.WriteReg(0x8c, 0x00);    // RGB444

	// turn off AWB
	unsigned char tmp = camera.ReadReg(0x13);	// COM8
	camera.WriteReg(0x13, tmp & 0xFD);			// COM8[1] = 0
	camera.WriteReg(0x02, 0x48);				// R gain
	camera.WriteReg(0x6A, 0x40);				// G gain
	camera.WriteReg(0x01, 0x60);				// B gain

    wait_ms(300);

	// discard some first frames
	for (int i = 0; i < 10; i++) {
	    camera.CaptureNext();   // sample start!
	    while(camera.CaptureDone() == false)
	        ;
	}

    //////////////////////////////////////////////////
	int	blur_kernel_size		= 7;

	float blur_matrix[] = {
		// Average filter
		// Change here in order to use a different filter
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
		0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f
	};

	int	prev_mem	= memfree();

	stream_filter *average_flt;
	average_flt	= create_stream_filter(0, SIZEX, blur_kernel_size, comb_filter, (void *)blur_matrix);	// 0: 1 plane

//	tmr.start();	// timer
//	cam_cap(0, average_flt);
//	tmr.stop();		// timer

	int	cur_mem	= memfree();

//	lcd.locate(0, 0);
//  lcd.printf("%dms", tmr.read_ms());

//	lcd.locate(0, 1);
//	lcd.printf("m %d, %d", prev_mem, cur_mem);

//char shot_flag = 0;

    //////////////////////////////////////////////////
    while (1) {
//		int	rgain, ggain, bgain; 
		int	skin_pixel_num;

		led1 = 1;
		reset_stream_filter(average_flt);

		tmr.reset();
		tmr.start();
		skin_pixel_num	= cam_cap(0, average_flt);
		tmr.stop();

		if (skin_pixel_num >= SHUTTER_THRESHOLD)	{	// threshold
			cam_cap(1, average_flt);	// shot

			lcd.locate(0, 0);
			lcd.printf("CAPTURED ");
			lcd.locate(0, 1);
			lcd.printf("val %d ", skin_pixel_num);

			while (1) {
				wait(1);
			}
		}
//		cur_mem	= memfree();

//		rgain	= camera.ReadReg(0x02);
//		ggain	= camera.ReadReg(0x6A);
//		bgain	= camera.ReadReg(0x01);

		lcd.locate(0, 0);
		lcd.printf("%dms ", tmr.read_ms());
		lcd.locate(0, 1);
		lcd.printf("val %d ", skin_pixel_num);
//		lcd.printf("%dms, M%d ", tmr.read_ms(), cur_mem);
//		lcd.printf("R%02x G%02x B%02x", rgain, ggain, bgain);

//		wait_ms(100);
   }

}


void write_bmp_header(FILE *fp_bmp)
{
    /////////////////////////
    // file header
    /////////////////////////
    fprintf(fp_bmp, "BM");
    int val = 14 + 40 + SIZEX * SIZEY * 3;   // file size
    fprintf(fp_bmp, "%c%c%c%c", val % 0x100, val / 0x100, val / 0x10000, val / 0x1000000);
    fprintf(fp_bmp, "%c%c%c%c%c%c%c%c", 0, 0, 0, 0, 0x36, 0, 0, 0);

    /////////////////////////
    // information header
    /////////////////////////
    fprintf(fp_bmp, "%c%c%c%c", 0x28, 0, 0, 0);  // header size
    fprintf(fp_bmp, "%c%c%c%c", SIZEX % 0x100, SIZEX / 0x100, SIZEX / 0x10000, SIZEX / 0x1000000);
    fprintf(fp_bmp, "%c%c%c%c", SIZEY % 0x100, SIZEY / 0x100, SIZEY / 0x10000, SIZEY / 0x1000000);
    fprintf(fp_bmp, "%c%c", 1, 0);               // # of plane
    fprintf(fp_bmp, "%c%c", 24, 0);              // bit count
    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);     // compression
    val = SIZEX * SIZEY * 3;         // data size
    fprintf(fp_bmp, "%c%c%c%c", val % 0x100, val / 0x100, val / 0x10000, val / 0x1000000);
    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
}


void write_bmp_data(FILE *fp_bmp, unsigned char b, unsigned char g, unsigned char r)
{
	fprintf(fp_bmp, "%c%c%c", b, g, r);
}


//void cam_cap(Arguments* input, Reply* output)
int cam_cap(char file_flag, stream_filter *average_flt)
{
	FILE *fp_bmp;
    unsigned int d1, d2;
    unsigned char sort[3];

	int	dx	= 0;
	int	dy	= 0;

	unsigned int	skin_pixel_num	= 0;
	unsigned int	skin_x			= 0;
	unsigned int	skin_y			= 0;


	////////////////////////////////////////////////////////////////////
	led2 = 0;
	led3 = 0;
	led4 = 0;

	if (file_flag != 0) {
	    fp_bmp	= fopen("/webfs/cam.bmp", "wb");
		write_bmp_header(fp_bmp);
	}

    camera.CaptureNext();   // sample start!

    while(camera.CaptureDone() == false)
        ;

    camera.ReadStart();     // reset pointer

	led2 = 1;

	/////////////////////////////////////////////
	// main loop
	/////////////////////////////////////////////
    for (int y = 0; y < SIZEY; y++) {    
        for (int x = 0; x < SIZEX; x++) {
            d1 = camera.ReadOneByte() ; // upper nibble is XXX , lower nibble is B
            d2 = camera.ReadOneByte() ; // upper nibble is G   , lower nibble is R

			/////////////////////////////////////////////
			// RGB565
			/////////////////////////////////////////////
			sort[0]	= ((d1 & 0xF8) >> 3) << 3;		// R
			sort[1] = ( ((d1 & 0x07) << 3) + ((d2 & 0xE0) >> 5) ) << 2;		// G
			sort[2]	= (d2 & 0x1F) << 3;				// B

			//////////////////////////////////////////////////////////////////////////
			// 1. get input
			//////////////////////////////////////////////////////////////////////////
			unsigned char	b0	= sort[2];
			unsigned char	g0	= sort[1];
			unsigned char	r0	= sort[0];

			if (file_flag != 0) {
				write_bmp_data(fp_bmp, sort[2], sort[1], sort[0]);		// B,G,R
			}
			else {
				//////////////////////////////////////////////////////////////////////////
				// 2. RGB2HSV
				//////////////////////////////////////////////////////////////////////////
				unsigned char	h1, s1, v1;
				rgb2hsv(r0, g0, b0, &h1, &s1, &v1);

				//////////////////////////////////////////////////////////////////////////
				// 3. judge
				//////////////////////////////////////////////////////////////////////////
				unsigned char	val2	= judge_skin_color(h1, s1, v1);

				//////////////////////////////////////////////////////////////////////////
				// 4. blur (average)
				//////////////////////////////////////////////////////////////////////////
				average_flt->d_in0	= val2;		// (1 plane mode; use d_in0 only)

				if (y == 0 && x == 0)
					average_flt->d_in_attr	= ATTR_1ST;
				else if (y == SIZEY - 1 && x == SIZEX - 1)
					average_flt->d_in_attr	= ATTR_LAST;
				else
					average_flt->d_in_attr	= ATTR_VALID;

				// apply filter
				apply_filter(average_flt);

				//////////////////////////////////////////////////////////////////////////
				// 5. limit
				//////////////////////////////////////////////////////////////////////////
				unsigned char	val3	= average_flt->d_out0;

				if (val3 < 220)
					val3	= 0;
				else {
					skin_pixel_num++;
					skin_x	+= dx;
					skin_y	+= dy;
				}

				//////////////////////////////////////////////////////////////////////////
				// 6. put output
				//////////////////////////////////////////////////////////////////////////
				if (average_flt->d_out_attr != ATTR_INVALID) {
					sort[2]	= val3 & b0;
					sort[1]	= val3 & g0;
					sort[0]	= val3 & r0;

					dx++;
					if (dx == SIZEX) {
						dx	= 0;
						dy++;
					}
				}
			}	// file_flag

        }
    }

	led3 = 1;

	if (file_flag != 0) {
		/////////////////////////////////////////////
		// flush filter
		/////////////////////////////////////////////
		average_flt->d_in_attr	= ATTR_INVALID;

		while (1) {
			apply_filter(average_flt);

			if (average_flt->d_out_attr == ATTR_INVALID)
				break;

			//////////////////////////////////////////////////////////////////////////
			// 5. limit
			//////////////////////////////////////////////////////////////////////////
			unsigned char	val3	= average_flt->d_out0;

			if (val3 < 220)
				val3	= 0;
			else {
				skin_pixel_num++;
				skin_x	+= dx;
				skin_y	+= dy;
			}

			//////////////////////////////////////////////////////////////////////////
			// 6. put output
			//////////////////////////////////////////////////////////////////////////
			sort[2]	= val3;
			sort[1]	= val3;
			sort[0]	= val3;

			dx++;
			if (dx == SIZEX) {
				dx	= 0;
				dy++;
			}
		}
	}

	led4 = 1;

	/////////////////////////////////////////////
	// end
	/////////////////////////////////////////////
    camera.ReadStop();

//	release_stream_filter(average_flt);

	if (skin_pixel_num != 0) {
		skin_x	/= skin_pixel_num;
		skin_y	/= skin_pixel_num;
	}
	else {
		skin_x	= SIZEX / 2;
		skin_y	= SIZEY / 2;
	}

	if (file_flag != 0) {
		fclose(fp_bmp);
	}

//	led2 = 0;
//	led3 = 0;
//	led4 = 0;

	return (skin_pixel_num);
}

// end of file