/// <summary> /// Extract blobs from an image in which distinct contiguous regions are marked /// with distinct pixel values such as returned by ippiLabelMarkers /// </summary> /// <param name="imMarkers">The marker image</param> /// <param name="nMarkers">The number of expected markers</param> /// <param name="imOriginal">The original image. Used for pixel-intensity information</param> /// <param name="roi">The roi in which to perform blob extraction</param> /// <returns>An array of discovered blobs</returns> public static BlobWithMoments[] ExtractBlobs(Image8 imMarkers, int nMarkers, IppiROI roi) { if (nMarkers == 0)//no point to iterate over the image if no marker was found { return(null); } BlobWithMoments[] blobs = new BlobWithMoments[nMarkers]; byte markerValue; //loop over ROI in marker image, adding pixels to their appropriate blob for (int i = roi.Y; i < roi.Y + roi.Height; i++) { for (int j = roi.X; j < roi.X + roi.Width; j++) { markerValue = imMarkers.Image[i * imMarkers.Stride + j]; if (markerValue != 0) //it belongs to a blob! { //since the lowest marker has the value 1 we need to deduct 1 from markerValue for 0-index based array mapping! if (blobs[markerValue - 1] == null) { blobs[markerValue - 1] = new BlobWithMoments(); } blobs[markerValue - 1].AddPixel(j, i); } } } return(blobs); }
/// <summary> /// Performs an opening operation in the specified ROI using the specified /// neighborhood mask /// </summary> /// <param name="imIn">The image to perform the opening operation on</param> /// <param name="imOpened">The image after the opening operation</param> /// <param name="imCalc">Intermediate buffer for processing</param> /// <param name="neighborhood">The mask for the opening operation</param> /// <param name="region">The image region in which to perform the operation</param> public static void Open(Image8 imIn, Image8 imOpened, Image8 imCalc, MorphologyMask neighborhood, IppiROI roi) { //Modify region we operate on to allow mask overhang var inner = new IppiROI(roi.X + neighborhood.Anchor.x, roi.Y + neighborhood.Anchor.y, roi.Width - neighborhood.Mask.Width, roi.Height - neighborhood.Mask.Height); IppHelper.IppCheckCall(ip.ippiErode_8u_C1R(imIn[inner.TopLeft], imIn.Stride, imCalc[inner.TopLeft], imCalc.Stride, inner.Size, neighborhood.Mask.Image, neighborhood.Mask.Size, neighborhood.Anchor)); IppHelper.IppCheckCall(ip.ippiDilate_8u_C1R(imCalc[inner.TopLeft], imCalc.Stride, imOpened[inner.TopLeft], imOpened.Stride, inner.Size, neighborhood.Mask.Image, neighborhood.Mask.Size, neighborhood.Anchor)); }
/// <summary> /// Performs a 3x3 opening operation on an image - removes speckles /// </summary> /// <param name="imIn">The image to perform an opening operation on</param> /// <param name="imOpened">The image after the opening operation</param> /// <param name="imCalc">Intermediate image for semi-processed version</param> /// <param name="region">The ROI in which to perform the operation</param> public static void Open3x3(Image8 imIn, Image8 imOpened, Image8 imCalc, IppiROI roi) { //Modify region we operate on to allow mask overhang var inner = new IppiROI(roi.X + 1, roi.Y + 1, roi.Width - 3, roi.Height - 3); IppHelper.IppCheckCall(ip.ippiErode3x3_8u_C1R(imIn[inner.TopLeft], imIn.Stride, imCalc[inner.TopLeft], imCalc.Stride, inner.Size)); IppHelper.IppCheckCall(ip.ippiDilate3x3_8u_C1R(imCalc[inner.TopLeft], imCalc.Stride, imOpened[inner.TopLeft], imOpened.Stride, inner.Size)); }
public virtual void UpdateBackground(Image8 im, BlobWithMoments regionToExclude) { if (regionToExclude == null) { UpdateBackground(im); } else { //reset our mask to <update all>, then fill bounding box with 0s ip.ippiSet_8u_C1R(byte.MaxValue, _updateMask.Image, _updateMask.Stride, _updateMask.Size); IppiROI roi = new IppiROI(regionToExclude.BoundingBox.x, regionToExclude.BoundingBox.y, regionToExclude.BoundingBox.width, regionToExclude.BoundingBox.height); ip.ippiSet_8u_C1R(0, _updateMask[regionToExclude.BB_TopLeft], _updateMask.Stride, regionToExclude.BB_Size); IppHelper.IppCheckCall(cv.ippiAddWeighted_8u32f_C1IMR(im.Image, im.Stride, _updateMask.Image, _updateMask.Stride, background.Image, background.Stride, im.Size, FractionUpdate)); } }
public virtual void UpdateBackground(Image8 im, Blob[] regionsToExclude) { if (regionsToExclude == null)//do a simple update { UpdateBackground(im); } else { //reset our mask to <update all>, then fill all bounding boxes with 0s ip.ippiSet_8u_C1R(byte.MaxValue, _updateMask.Image, _updateMask.Stride, _updateMask.Size); foreach (Blob b in regionsToExclude) { if (b != null) { IppiROI roi = new IppiROI(b.BoundingBox); ip.ippiSet_8u_C1R(0, _updateMask[roi.TopLeft], _updateMask.Stride, roi.Size); } } IppHelper.IppCheckCall(cv.ippiAddWeighted_8u32f_C1IMR(im.Image, im.Stride, _updateMask.Image, _updateMask.Stride, background.Image, background.Stride, im.Size, FractionUpdate)); } }
/// <summary> /// Implements a "greater than" threshold like MATLABS /// im2bw function /// </summary> /// <param name="imIn">The image to threshold</param> /// <param name="imThresh">The image after thresholding</param> /// <param name="region">The ROI in which to perform the operation</param> /// <param name="threshold">The threshold to apply</param> public static void Im2Bw(Image8 imIn, Image8 imThresh, IppiROI region, byte threshold) { IppHelper.IppCheckCall(ip.ippiThreshold_LTVal_8u_C1R(imIn[region.TopLeft], imIn.Stride, imThresh[region.TopLeft], imThresh.Stride, region.Size, (byte)(threshold + 1), 0)); IppHelper.IppCheckCall(ip.ippiThreshold_GTVal_8u_C1IR(imThresh[region.TopLeft], imThresh.Stride, region.Size, threshold, 255)); }
void GenerateMask() { //To generate the mask, we use a euclidian distance transform to the center pixel //followed by a thresholding operation //To save resources we only do this within the outer square of the circle since //all pixels outside of that square will be black anyway double xStart, yStart; //compute outer square xStart = _center.x - _radius; yStart = _center.y - _radius; if (xStart < 0) { xStart = 0; } if (yStart < 0) { yStart = 0; } IppiROI outerSquare = new IppiROI((int)xStart, (int)yStart, 2 * _radius, 2 * _radius); if (outerSquare.X + outerSquare.Width >= _mask.Width) { outerSquare.Width = _mask.Width - outerSquare.X - 1; } if (outerSquare.Y + outerSquare.Height >= _mask.Height) { outerSquare.Height = _mask.Height - outerSquare.Y - 1; } //The distance transform will calculate for all non-0 pixels the distance to the closest //0 pixel - hence we set all pixels in our mask temporarily to 1 and the circles center to 0 IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(1, _mask.Image, _mask.Stride, _mask.Size)); IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(0, _mask[Center], _mask.Stride, new IppiSize(1, 1))); //initialize buffers and calculate distance transform within outer square //we initialize the distance transform image with a value > radius so that all the //untouched pixel outside of the outer square will later be set to 0 int bufferSize; IppHelper.IppCheckCall(cv.ippiTrueDistanceTransformGetBufferSize_8u32f_C1R(outerSquare.Size, &bufferSize)); byte * dTransBuffer = (byte *)Marshal.AllocHGlobal(bufferSize); Image32F distImage = new Image32F(_mask); IppHelper.IppCheckCall(ip.ippiSet_32f_C1R(Radius + 1, distImage.Image, distImage.Stride, distImage.Size)); IppHelper.IppCheckCall(cv.ippiTrueDistanceTransform_8u32f_C1R(_mask[outerSquare.TopLeft], _mask.Stride, distImage[outerSquare.TopLeft], distImage.Stride, outerSquare.Size, dTransBuffer)); //set all pixels whose value after the distance transform is >radius to 0 IppHelper.IppCheckCall(ip.ippiThreshold_GTVal_32f_C1IR(distImage.Image, distImage.Stride, distImage.Size, Radius, 0)); //now set all pixels whose value is >0 to 1 IppHelper.IppCheckCall(ip.ippiThreshold_GTVal_32f_C1IR(distImage.Image, distImage.Stride, distImage.Size, 0, 1)); //convert dist image, copying it into the mask IppHelper.IppCheckCall(ip.ippiConvert_32f8u_C1R(distImage.Image, distImage.Stride, _mask.Image, _mask.Stride, _mask.Size, IppRoundMode.ippRndNear)); //Clean up Marshal.FreeHGlobal((IntPtr)dTransBuffer); distImage.Dispose(); _maskValid = true; /* * //We want to generate the mask with the least amount of pixel-iterations possible * //based on the radius we can define to rectangular regions for which we can * //bulk-set the pixels * //The inner square of the circle is definitely white (centered around center, side-length = sqrt(r*r/2) * //The outer square is definitely black (centerered around center, side-length=radius) * double sideLength,xStart,yStart; * * //compute inner square * sideLength = Math.Sqrt(_radius * _radius / 2); * xStart = _center.x - sideLength / 2; * yStart = _center.y - sideLength / 2; * if (xStart < 0) * xStart = 0; * if (yStart < 0) * yStart = 0; * IppiROI innerSquare = new IppiROI((int)xStart, (int)yStart, (int)sideLength, (int)sideLength); * if (innerSquare.X + innerSquare.Width >= _mask.Width) * { * innerSquare.Width = _mask.Width - innerSquare.X - 1; * } * if (innerSquare.Y + innerSquare.Height >= _mask.Height) * { * innerSquare.Height = _mask.Height - innerSquare.Y - 1; * } * * //compute outer square * xStart = _center.x - _radius / 2.0; * yStart = _center.y - _radius / 2.0; * if (xStart < 0) * xStart = 0; * if (yStart < 0) * yStart = 0; * IppiROI outerSquare = new IppiROI((int)xStart, (int)yStart, _radius, _radius); * if (outerSquare.X + outerSquare.Width >= _mask.Width) * { * outerSquare.Width = _mask.Width - outerSquare.X - 1; * } * if (outerSquare.Y + outerSquare.Height >= _mask.Height) * { * outerSquare.Height = _mask.Height - outerSquare.Y - 1; * } * * //set whole image to black and inner square to 1 * IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(0, _mask.Image, _mask.Stride, _mask.Size)); * IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(1, _mask[innerSquare.TopLeft], _mask.Stride, innerSquare.Size)); * * //loop over all the pixels that are in between the boundaries of the outer * //and inner square, measure their distance to the center and * //p = d>r?0:1; */ }