/*----------------------------------------------------------------------- */
SEXP
lib_erode_dilate (SEXP x, SEXP kernel, SEXP what, SEXP binary) {
    numeric resetTo, * tgt, * src, *kern, min, max;
    int nz, i, j, nprotect;
    int * dim;
    PointXY size, ksize, pt;
    SEXP res;

    validImage(x,0);
    validImage(kernel,0);

    /* value to reset the checked part t */
    if ( INTEGER(what)[0] == DILATE )
        resetTo = 1.0; /* checking background, reseting to 1 */
    else
        resetTo = 0.0; /* checking foreground, reseting to 0 */
    dim = INTEGER ( GET_DIM(x) );
    size.x = dim[0];
    size.y = dim[1];
    nz = getNumberOfFrames(x,0);

    kern = REAL (kernel);
    ksize.x = INTEGER ( GET_DIM(kernel) )[0];
    ksize.y = INTEGER ( GET_DIM(kernel) )[1];
    nprotect = 0;

    PROTECT ( res = Rf_duplicate(x) );
    nprotect++;

    for ( i = 0; i < nz; i++ ) {
        tgt = &( REAL(res)[i * size.x * size.y] );
        src = &( REAL(x)[i * size.x * size.y] );

        if ( ! INTEGER(binary)[0] ) {
            min = max = src[0];
            for ( j = 0; j < size.x * size.y; j++ ) {
                if (src[j] > max)
                    max = src[j];
                if (src[j] < min)
                    min = src[j];
            }
            for ( j = 0; j < size.x * size.y; j++ ) {
                pt = pointFromIndex (j, size.x);
                tgt[j] = _greymatch(kern, &ksize, src, &size, &pt,
                                    INTEGER(what)[0], min , max);
            }
        }
        else {
            for ( j = 0; j < size.x * size.y; j++ ) {
                if ( tgt[j] == resetTo ) continue;
                pt = pointFromIndex (j, size.x);
                if ( !_match(kern, &ksize, src, &size, &pt, resetTo) )
                    tgt[j] = resetTo;
            }
        }
    }
    
    UNPROTECT (nprotect);
    return res;
}
Beispiel #2
0
static void TestBitmapSerialization(const SkBitmap& validBitmap,
                                    const SkBitmap& invalidBitmap,
                                    bool shouldSucceed,
                                    skiatest::Reporter* reporter) {
    sk_sp<SkImage> validImage(SkImage::MakeFromBitmap(validBitmap));
    sk_sp<SkImageFilter> validBitmapSource(SkImageSource::Make(std::move(validImage)));
    sk_sp<SkImage> invalidImage(SkImage::MakeFromBitmap(invalidBitmap));
    sk_sp<SkImageFilter> invalidBitmapSource(SkImageSource::Make(std::move(invalidImage)));
    sk_sp<SkImageFilter> xfermodeImageFilter(
        SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver,
                                    std::move(invalidBitmapSource),
                                    std::move(validBitmapSource), nullptr));

    sk_sp<SkImageFilter> deserializedFilter(
        TestFlattenableSerialization<SkImageFilter>(
            xfermodeImageFilter.get(), shouldSucceed, reporter));

    // Try to render a small bitmap using the invalid deserialized filter
    // to make sure we don't crash while trying to render it
    if (shouldSucceed) {
        SkBitmap bitmap;
        bitmap.allocN32Pixels(24, 24);
        SkCanvas canvas(bitmap);
        canvas.clear(0x00000000);
        SkPaint paint;
        paint.setImageFilter(deserializedFilter);
        canvas.clipRect(SkRect::MakeXYWH(0, 0, SkIntToScalar(24), SkIntToScalar(24)));
        canvas.drawBitmap(bitmap, 0, 0, &paint);
    }
}
Beispiel #3
0
/* -------------------------------------------------------------------------- */
SEXP
fillHull(SEXP x) {
  SEXP res;
  int nprotect = 0;
  int nz;

  // check image validity
  validImage(x,0);
  nz = getNumberOfFrames(x, 0);

  int *dim=INTEGER(GET_DIM(x));
  XYPoint size(dim[0], dim[1]);


  // return itself if nothing to do
  if (size.x <= 0 || size.y <= 0 || nz < 1) return x;
 
  // do fillHull
  PROTECT(res = Rf_duplicate(x));
  nprotect++;
  if (IS_INTEGER(res)) {
    for (int i=0; i < nz; i++) _fillHullT<int>(&(INTEGER(res)[i*size.x*size.y]), size);
  }
  else if (IS_NUMERIC(res)) {
    for (int i=0; i < nz; i++) _fillHullT<double>(&(REAL(res)[i*size.x*size.y]), size);
  }
  
  UNPROTECT (nprotect);
  return res;
}
Beispiel #4
0
/* -------------------------------------------------------------------------- */
SEXP
floodFill(SEXP x, SEXP point, SEXP col, SEXP tol) {
  int i, nz, *dim;
  int nprotect=0;
  XYPoint pt;
  SEXP res;

  // check image validity
  validImage(x,0);
  nz = getNumberOfFrames(x, 0);
  dim = INTEGER(GET_DIM(x));
  XYPoint size(dim[0], dim[1]);
  if (size.x <= 0 || size.y <= 0) error("image must have positive dimensions");
  if (LENGTH(point) != 2*nz) error("point must have a size of two times the number of frames");
  if (LENGTH(col) != nz) error("color must have the same size as the number of frames");
  
  // initialize result
  PROTECT(res = Rf_duplicate(x));
  nprotect++;
  
  // do the job over images
 for (i=0; i<nz; i++) {
    pt.x = INTEGER(point)[i]-1;
    pt.y = INTEGER(point)[nz+i]-1;
    
    if (IS_NUMERIC(res)) _floodFill<double>(&(REAL(res)[i*size.x*size.y]), size, pt, REAL(col)[i], REAL(tol)[0]);
    if (IS_INTEGER(res)) _floodFill<int>(&(INTEGER(res)[i*size.x*size.y]), size, pt, INTEGER(col)[i], REAL(tol)[0]);
  }

  UNPROTECT (nprotect);
  return res;
}
SEXP lib_display(SEXP x, SEXP caption, SEXP useGTK) {
#ifndef WIN32
    pthread_t res;
#endif

    validImage(x,0);

#ifdef USE_GTK
    if ( LOGICAL(useGTK)[0] ) {
        if ( GTK_OK )
            _showInGtkWindow (x, caption);
        else
            error ( "GTK+ was not properly initialised" );
        return R_NilValue;
    }
#endif

#ifdef WIN32
    error ( "ImageMagick 'display' is not available on Windows" );
#else
    if ( THREAD_ON )
        error ( "Cannot display multiple windows. Please close the currently displayed window first." );
    if ( pthread_create(&res, NULL, _showInImageMagickWindow, (void *)x ) != 0 )
        error ( "Failed to create 'display' thread" );
#endif
    return R_NilValue;
}
Beispiel #6
0
/*----------------------------------------------------------------------- */
SEXP
lib_writeImages (SEXP x, SEXP files, SEXP quality) {
    int nz, nfiles, i;
    Image * images, * image;
    ImageInfo *image_info;
    ExceptionInfo exception;

    /* basic checks */
    validImage(x,0);

    images = sexp2Magick (x);
    nz = GetImageListLength(images);
 
    nfiles = LENGTH (files);
    if ( nfiles != 1 && nfiles != nz)
        error ( "number of files must be 1, or equal to the size of the image stack" );
    
    if ( images == NULL || GetImageListLength (images) < 1 )
        error ( "cannot write an empty image" );
    GetExceptionInfo (&exception);
    image_info = CloneImageInfo ( (ImageInfo *)NULL );
    /* set attributes in image_info*/
    image_info->compression = images->compression;
    image_info->quality = (unsigned int) INTEGER (quality)[0];
    if ( nfiles == 1 ) {
    /* save into a single file, TIFF, GIF, or automatically add file suffixes */
        strcpy (image_info->filename, CHAR(STRING_ELT(files, 0)) );
        /* we want to overwrite the feature imported from SEXP image */
        strcpy (images->filename, image_info->filename);
        WriteImages(image_info, images, CHAR(STRING_ELT(files, 0)), &exception);
        CatchException (&exception);
    }
    else {
    /* save each frame into a separate file */
        for ( i = 0; i < nz; i++ ) {
            image = GetImageFromList (images, i);
            if ( image == NULL || GetImageListLength (image) < 1 ) {
                warning ( "cannot write an empty image, skipping" );
                continue;
            }
            strcpy (image_info->filename, CHAR(STRING_ELT(files, i)));
            /* we want to overwrite the feature imported from SEXP image */
            strcpy (image->filename, image_info->filename);
            WriteImage (image_info, image);
            CatchException (&image->exception);
            // WriteImages(image_info, image, CHAR(STRING_ELT(files, i)), &exception);
            // CatchException (&exception);

        }
    }

    image_info = DestroyImageInfo (image_info);
    images = DestroyImageList (images);
    DestroyExceptionInfo(&exception);
    return R_NilValue;
}
Beispiel #7
0
// Compute Euclidean (L2)/Manhattan (L1) distance map of matrix _a 
// Input: numeric matrix _a, of size width*height, where 0 is background and everything else is foreground. _a shouldn't contain any NAs
// Input: integer _metric. If 0, will compute Euclidean distance and Manhattan distance otherwise
// Output: distance matrix of same size as _a
SEXP distmap(SEXP _a, SEXP _metric) {
  SEXP res;
  int i,nprotect=0,nz;
  
  // check validity
  validImage(_a,0);
  
  // initialize width, height, dim
  width=INTEGER(GET_DIM(_a))[0];
  height=INTEGER(GET_DIM(_a))[1];
  nz=getNumberOfFrames(_a,0);
  
  // initialize vj, where (i,vj[i]) are the coordinates of the closest background pixel to a(i,j) with vj[i]>=j
  vj=(int *)R_Calloc(height,int);
  
  // initialize d, the output distance matrix
  PROTECT(res = allocVector(REALSXP, XLENGTH(_a)) );
  nprotect++;
  DUPLICATE_ATTRIB(res, _a);
  
  d=REAL(res);
  for (i=0;i<height*width*nz;i++) d[i]=R_PosInf;
  
  // initialize dist, the distance type
  metric=INTEGER(_metric)[0];
  
  // do the job
  int sizexy = height*width;
  int offset = 0;
  
  for (i=0; i<nz; i++, offset+=sizexy) {
    d = &(REAL(res)[offset]);
    
    switch (TYPEOF(_a)) {
    case LGLSXP:
    case INTSXP:
      _distmap<int>( &(INTEGER(_a)[offset]) );
      break;
    case REALSXP:
      _distmap<double>( &(REAL(_a)[offset]) );
      break;
    }
  }
  
  // final square root for Euclidean distance
  d=REAL(res);
  if (metric==0) for (i=0;i<height*width*nz;i++) d[i]=sqrt(d[i]);
  
  R_Free(vj);
  
  UNPROTECT (nprotect);
  return res;
}
Beispiel #8
0
SEXP affine(SEXP _a, SEXP _b, SEXP _m, SEXP _filter) {
  int width, height, nz;
  int owidth, oheight;
  int filter;
  double *a, *m, *b;

  // check image validity
  validImage(_a, 0); 

  // initialize width, height, nz
  width = INTEGER(GET_DIM(_a))[0];
  height = INTEGER(GET_DIM(_a))[1];
  nz = getNumberOfFrames(_a, 0);
 
  // initialize a, m, filter
  a = REAL(_a);
  m = REAL(_m);
  filter = INTEGER(_filter)[0];

  // get output image b data
  owidth = INTEGER(GET_DIM(_b))[0];
  oheight = INTEGER(GET_DIM(_b))[1];
  b = REAL(_b);

  // apply transform
  for (int z=0; z<nz; z++) {
    for (int y=0; y<oheight; y++) { 
      for (int x=0; x<owidth; x++) {
        int idx = x + y*owidth + z*owidth*oheight;
        double bg = b[idx];
      	double tx = m[0]*x + m[1]*y + m[2];
      	double ty = m[3]*x + m[4]*y + m[5];
      	int ftx = floor(tx);
      	int fty = floor(ty);
      	double dx = tx-ftx;
      	double dy = ty-fty;
      	double pa = peekpixel(ftx, fty, z, width, height, a, bg);
      	// bilinear filter?
      	if (filter==1) {
      	  double pb = peekpixel(ftx+1, fty, z, width, height, a, bg);
      	  double pc = peekpixel(ftx, fty+1, z, width, height, a, bg);
      	  double pd = peekpixel(ftx+1, fty+1, z, width, height, a, bg);
      	  pa = (1-dy)*(pa*(1-dx) + pb*dx) + dy*(pc*(1-dx) + pd*dx);
      	}
      	b[idx] = pa;
      }
    }
  }

  return _b;
} 
/* -------------------------------------------------------------------------- */
SEXP
bwlabel(SEXP x) {
  int i, kx, ky, nz, *dim;
  int nprotect=0;
  double index;
  XYPoint pt;
  SEXP res;

  // check image validity
  validImage(x,0);
  nz = getNumberOfFrames(x, 0);
  dim = INTEGER(GET_DIM(x));
  XYPoint size(dim[0], dim[1]);
  if (size.x <= 0 || size.y <= 0) error("image must have positive dimensions");
  
  // initialize result
  PROTECT(res = Rf_duplicate(x));
  nprotect++;  

  // assuming binary images: 0 is background and everything else foreground
  // foreground is converted here to -1.0
  // NO NO NO: I've splitted some labelled objects, I want to relabel the parts,
  // so I put them to -REAL(res)[i] instead of -1, otherwise they will be merged
  // as they are connected
  for (i=0; i<nz*size.x*size.y; i++) {
    if (REAL(res)[i]!=0.0) REAL(res)[i]=-REAL(res)[i];
  }
  
  // do the job over images
  // every pixel equals with R_PosInf is filled with an increasing index, starting from 1
  for (i=0; i<nz; i++) {
    index = 1.0;
    for (ky=0; ky<size.y ; ky++) {
      for (kx=0; kx<size.x ; kx++) {
          if ( REAL(res)[kx + ky*size.x + i*size.x*size.y] < 0 ) {
	  pt.x = kx;
	  pt.y = ky;
	  _floodFill<double>(&(REAL(res)[i*size.x*size.y]), size, pt, index, 0.0);
	  index = index + 1.0;
	}
      }
    }
  }

  UNPROTECT (nprotect);
  return res;
}
SEXP lib_animate (SEXP x) {
#ifndef WIN32
    pthread_t res;
#endif

    validImage(x,0);

#ifdef WIN32
    error ( "ImageMagick 'animate' is not available on Windows." );
#else
    if ( THREAD_ON )
        error ( "Cannot display multiple windows. Please close the currently displayed window first." );
    if ( pthread_create(&res, NULL, _animateInImageMagickWindow, (void *)x ) != 0 )
        error ( "Failed to create 'animate' thread" );
#endif
    return R_NilValue;
}
Beispiel #11
0
// Compute Euclidean (L2)/Manhattan (L1) distance map of matrix _a 
// Input: numeric matrix _a, of size width*height, where 0 is background and everything else is foreground. _a shouldn't contain any NAs
// Input: integer _metric. If 0, will compute Euclidean distance and Manhattan distance otherwise
// Output: distance matrix of same size as _a
SEXP distmap(SEXP _a, SEXP _metric) {
  SEXP res;
  int i,nprotect=0,nz;

  // check validity
  validImage(_a,0);

  // initialize width, height, dim
  width=INTEGER(GET_DIM(_a))[0];
  height=INTEGER(GET_DIM(_a))[1];
  nz=getNumberOfFrames(_a,0);

 // initialize vj, where (i,vj[i]) are the coordinates of the closest background pixel to a(i,j) with vj[i]>=j
  vj=(int *)R_Calloc(height,int);

  // initialize a
  a=REAL(_a);

  // initialize d, the output distance matrix
  PROTECT(res=Rf_duplicate(_a));
  nprotect++;
  d=REAL(res);
  for (i=0;i<height*width*nz;i++) d[i]=R_PosInf;
  
  // initialize dist, the distance type
  metric=INTEGER(_metric)[0];
   
  // do the job
  for (i=0;i<nz;i++) {
    distmap_onesided(1);
    distmap_onesided(0);
    a=a+height*width;
    d=d+height*width;
  }

  // final square root for Euclidean distance
  d=REAL(res);
  if (metric==0) for (i=0;i<height*width*nz;i++) d[i]=sqrt(d[i]);

  // free vj
  R_Free(vj);

  UNPROTECT (nprotect);
  return res;
}
uint8_t processPacket()
{
#endif

	uint8_t buffer[TFTP_PACKET_MAX_SIZE];
	uint16_t readPointer;
	uint16_t writeAddr;
	// Transfer entire packet to RAM
	uint8_t *bufPtr = buffer;
	uint16_t count;

#ifdef _DEBUG_TFTP
	traceln("Tftp: ----");
	traceln("Tftp: Starting processing packet of size ");
	tracenum(packetSize);
	if(packetSize >= 0x800) traceln("Tftp: Overflow");
	//  step();
#endif

	readPointer = netReadWord(REG_S3_RX_RD0);
#ifdef _DEBUG_TFTP
	traceln("Tftp: readPointer at position ");
	tracenum(readPointer);
#endif
	if(readPointer == 0) readPointer += S3_RX_START;
	for(count = TFTP_PACKET_MAX_SIZE; count--;) {
#ifdef _DEBUG_TFTP
		if((count == TFTP_PACKET_MAX_SIZE - 1) || (count == 0)) {
			traceln("Tftp: Reading from position ");
			tracenum(readPointer);
		}
#endif
		*bufPtr++ = netReadReg(readPointer++);
		if(readPointer == S3_RX_END) readPointer = S3_RX_START;
	}
	netWriteWord(REG_S3_RX_RD0, readPointer);     // Write back new pointer
	netWriteReg(REG_S3_CR, CR_RECV);
	while(netReadReg(REG_S3_CR));
#ifdef _DEBUG_TFTP
	traceln("Tftp: Bytes left to read ");
	tracenum(netReadWord(REG_S3_RX_RSR0));
#endif

#ifdef _DEBUGMORE_TFTP
	// Dump packet
	bufPtr = buffer;
	traceln("");
	for(count = TFTP_PACKET_MAX_SIZE / 2; count--;) {
		uint16_t val = *bufPtr++;
		val |= (*bufPtr++) << 8;
		tracenum(val);
		if((count % 8) == 0 && count != 0) traceln("");
		else trace(" ");
	}
#endif

#ifdef _DEBUG_TFTP
	traceln("Tftp: Setting return address");
#endif

	// Set up return IP address and port
	uint8_t i;
	for(i = 0; i < 6; i++) netWriteReg(REG_S3_DIPR0 + i, buffer[i]);

	// Parse packet
	uint16_t tftpDataLen = (buffer[6] << 8) + buffer[7];
	uint16_t tftpOpcode = (buffer[8] << 8) + buffer[9];
	uint16_t tftpBlock = (buffer[10] << 8) + buffer[11];
#ifdef _DEBUG
	traceln("Tftp: This is block ");
	tracenum(tftpBlock);
	trace(" with opcode ");
	tracenum(tftpOpcode);
	trace(" and data length ");
	tracenum(tftpDataLen - (TFTP_OPCODE_SIZE + TFTP_BLOCKNO_SIZE));
#endif

	uint8_t returnCode = ERROR_UNKNOWN;
	uint16_t packetLength;


	switch(tftpOpcode) {

		case TFTP_OPCODE_RRQ: // Read request
#ifdef _DEBUG_TFTP
			traceln("Tftp: Read request");
#endif
			break;

		case TFTP_OPCODE_WRQ: // Write request
#ifdef _VERBOSE
			traceln("Tftp: Write request");
#endif
			// Flagging image as invalid since the flashing process has started
			eeprom_write_byte(EEPROM_IMG_STAT, EEPROM_IMG_BAD_VALUE);
			netWriteReg(REG_S3_CR, CR_RECV);
			netWriteReg(REG_S3_CR, CR_CLOSE);
			do {
				netWriteReg(REG_S3_MR, MR_UDP);
				netWriteReg(REG_S3_CR, CR_OPEN);
#ifdef _TFTP_RANDOM_PORT
				netWriteWord(REG_S3_PORT0, (buffer[4]<<8) | ~buffer[5]); // Generate a 'random' TID (RFC1350)
#else
				netWriteWord(REG_S3_PORT0, tftpPort);
#endif
				if(netReadReg(REG_S3_SR) != SOCK_UDP)
					netWriteReg(REG_S3_CR, CR_CLOSE);
			} while(netReadReg(REG_S3_SR) != SOCK_UDP);
#ifdef _DEBUG_TFTP
			traceln("Tftp: Changed to port ");
#ifdef _TFTP_RANDOM_PORT
			tracenum((buffer[4]<<8) | (buffer[5]^0x55));
#else
			tracenum(tftpPort);
#endif
#endif
			lastPacket = 0;
			returnCode = ACK; // Send back acknowledge for packet 0
			break;

		case TFTP_OPCODE_DATA:
			packetLength = tftpDataLen - (TFTP_OPCODE_SIZE + TFTP_BLOCKNO_SIZE);
			lastPacket = tftpBlock;
			writeAddr = (tftpBlock - 1) << 9; // Flash write address for this block
#ifdef _VERBOSE
			traceln("Tftp: Data for block ");
			tracenum(lastPacket);
#endif

			if((writeAddr + packetLength) > MAX_ADDR) {
				// Flash is full - abort with an error before a bootloader overwrite occurs
				// Application is now corrupt, so do not hand over.
#ifdef _VERBOSE
				traceln("Tftp: Flash is full");
#endif
				returnCode = ERROR_FULL;
			} else {
#ifdef _DEBUG_TFTP
				traceln("Tftp: Writing data from address ");
				tracenum(writeAddr);
#endif

				uint8_t *pageBase = buffer + (UDP_HEADER_SIZE + TFTP_OPCODE_SIZE + TFTP_BLOCKNO_SIZE); // Start of block data
				uint16_t offset = 0; // Block offset

				// Round up packet length to a full flash sector size
				while(packetLength % SPM_PAGESIZE) packetLength++;
#ifdef _DEBUG_TFTP
				traceln("Tftp: Packet length adjusted to ");
				tracenum(packetLength);
#endif
				if(writeAddr == 0) {
					// First sector - validate
					if(!validImage(pageBase)) {
						returnCode = INVALID_IMAGE;
						/* FIXME: Validity checks. Small programms (under 512 bytes?) don't
						 * have the the JMP sections and that is why app.bin was failing.
						 * When flashing big binaries is fixed, uncomment the break below.*/
#ifndef _DEBUG_TFTP
						break;
#endif
					}
				}

				// Flash packets
				for(offset = 0; offset < packetLength;) {
					uint16_t writeValue = (pageBase[offset]) | (pageBase[offset + 1] << 8);
					boot_page_fill(writeAddr + offset, writeValue);
#ifdef _DEBUGMORE
					if((offset == 0) || ((offset == (packetLength - 2)))) {
						traceln("Tftp: Writing ");
						tracenum(writeValue);
						trace(" at offset ");
						tracenum(writeAddr + offset);
					}
#endif
					offset += 2;
					if(offset % SPM_PAGESIZE == 0) {
						boot_page_erase(writeAddr + offset - SPM_PAGESIZE);
						boot_spm_busy_wait();
						boot_page_write(writeAddr + offset - SPM_PAGESIZE);
						boot_spm_busy_wait();
						boot_rww_enable();
					}
				}

				if(packetLength < TFTP_DATA_SIZE) {
					// Flash is complete
					// Hand over to application
#ifdef _VERBOSE
					traceln("Tftp: Flash is complete");
#endif
					// Flag the image as valid since we received the last packet
					eeprom_write_byte(EEPROM_IMG_STAT, EEPROM_IMG_OK_VALUE);
					returnCode = FINAL_ACK;
				} else {
					returnCode = ACK;
				}
			}
			break;

			// Acknowledgment
		case TFTP_OPCODE_ACK:
#ifdef _DEBUG_TFTP
			traceln("Tftp: Acknowledge");
#endif
			break;

			// Error signal
		case TFTP_OPCODE_ERROR:
#ifdef _DEBUG_TFTP
			traceln("Tftp: Error");
#endif
			break;

		default:
#ifdef _DEBUG_TFTP
			traceln("Tftp: Invalid opcode ");
			tracenum(tftpOpcode);
#endif
			// Invalid - return error
			returnCode = ERROR_INVALID;
			break;

	}
	return(returnCode);
}
/*----------------------------------------------------------------------- */
SEXP
thresh (SEXP x, SEXP param) {
    int dx, dy, nx, ny, nz, nprotect, * dim, xi, yi, u, v, i;
    int sx, ex, sy, ey;
    double offset, * tgt, * src, sum, mean, nFramePix;
    SEXP res;
    
    validImage(x,0);

    dim = INTEGER ( GET_DIM(x) );
    nx = dim[0];
    ny = dim[1];
    nz = getNumberOfFrames(x,0);

    dx = (int)( REAL(param)[0] );
    dy = (int)( REAL(param)[1] );
    offset = REAL(param)[2];
    nprotect = 0;
    nFramePix = (2 * dx + 1) * (2 * dy + 1);

    PROTECT ( res = Rf_duplicate(x) );
    nprotect++;

    for ( i = 0; i < nz; i++ ) {
        tgt = &( REAL(res)[ i * nx * ny ] );
        src = &( REAL(x)[ i * nx * ny ] );
        for ( yi = dy; yi < ny - dy; yi++ ) {
            sum = 0.0;
            for ( xi = dx; xi < nx - dx; xi++ ) {
                if ( xi == dx) {
                /* first position in a row -- collect new sum */
                    for ( u = xi - dx; u <= xi + dx; u++ )
                        for ( v = yi - dy; v <= yi + dy; v++ )
                            sum += src [u + v * nx];
                }
                else {
                /* frame moved in the row, modify sum */
                    for ( v = yi - dy; v <= yi + dy; v++ )
                        sum += src [xi + dx + v * nx] - src [ xi - dx - 1 + v * nx];
                }
                /* calculate threshold and update tgt data */
                mean = sum / nFramePix + offset;
                sx = xi;
                ex = xi;
                sy = yi;
                ey = yi;
                if ( xi == dx ) {
                    /* left */
                    sx = 0;
                    ex = dx;
                }
                else
                if ( xi == nx - dx - 1 ) {
                    /* right */
                    sx = nx - dx - 1;
                    ex = nx - 1;
                }
                if ( yi == dy ) {
                    /* top */
                    sy = 0;
                    ey = dy;
                }
                else
                if ( yi == ny - dy - 1 ) {
                    /* bottom */
                    sy = ny - dy - 1;
                    ey = ny - 1;
                }
                if ( ex - sx > 0 || ey - sy > 0 ) {
                    for ( u = sx; u <= ex; u++ )
                        for ( v = sy; v <= ey; v++ )
                            tgt [u + v * nx] = ( src [u + v * nx] < mean ) ? BG : FG;
                }
                else /* thresh current pixel only */
                    tgt [xi + yi * nx] = ( src [xi + yi * nx] < mean ) ? BG : FG;
            }
        }
    }

    UNPROTECT (nprotect);
    return res;
}