//////////////////////////////////////////////////////////////////////////////// // // Function Name: IVA_Classification_Segmentation // // Description : Segments the classification image // // Parameters : image - Input Image // imageMask - Segmented image // roi - Region of Interest // preprocessingOptions - Preprocessing options // // Return Value : None // //////////////////////////////////////////////////////////////////////////////// public static void IVA_Classification_Segmentation(VisionImage image, VisionImage imageMask, Roi roi, ParticleClassifierPreprocessingOptions preprocessingOptions) { int useExpandedROI = 0; RectangleContour boundingBox = roi.GetBoundingRectangle(), expandedBox = roi.GetBoundingRectangle(), reducedBox = roi.GetBoundingRectangle(); // Local Threshold method uses a kernel size, and the ROI must be larger than the kernel size for the function to work // Take care of making the ROI to extract larger if needed for the local threshold case if (preprocessingOptions.ThresholdType == ThresholdType.Local) { // Get the image size int xRes, yRes; xRes = image.Width; yRes = image.Height; boundingBox = roi.GetBoundingRectangle(); // Take into account clipping of ROI. Just get ROI that's within bounds of image. if (Math.Min(xRes, (boundingBox.Left + boundingBox.Width)) - Math.Max(0, boundingBox.Left) < preprocessingOptions.LocalThresholdOptions.WindowWidth || Math.Min(yRes, (boundingBox.Top + boundingBox.Height)) - Math.Max(0, boundingBox.Top) < preprocessingOptions.LocalThresholdOptions.WindowHeight) { // The ROI is smaller than the kernel. Try to expand the kernel in the directions needed to meet the minimum size required by the kernel. int expandX = (int)(preprocessingOptions.LocalThresholdOptions.WindowWidth / 2); int expandY = (int)(preprocessingOptions.LocalThresholdOptions.WindowHeight / 2); int leftExpand = expandX; int rightExpand = expandX; int leftSlack = (int)boundingBox.Left; int rightSlack = (int)(xRes - (boundingBox.Left + boundingBox.Width)); if (leftExpand > leftSlack) { rightExpand += (leftExpand - leftSlack); } if (rightExpand > rightSlack) { leftExpand += (rightExpand - rightSlack); } int leftOut = (int)boundingBox.Left - leftExpand; if (leftOut < 0) { leftOut = 0; } int rightOut = (int)(boundingBox.Left + boundingBox.Width + rightExpand); if (rightOut > xRes) { rightOut = xRes; } int topExpand = expandY; int bottomExpand = expandY; int topSlack = (int)boundingBox.Top; int bottomSlack = (int)(yRes - (boundingBox.Top + boundingBox.Height)); if (topExpand > topSlack) { bottomExpand += (topExpand - topSlack); } if (bottomExpand > bottomSlack) { topExpand += (bottomExpand - bottomSlack); } int topOut = (int)(boundingBox.Top - topExpand); if (topOut < 0) { topOut = 0; } int bottomOut = (int)(boundingBox.Top + boundingBox.Height + bottomExpand); if (bottomOut > yRes) { bottomOut = yRes; } expandedBox.Initialize(leftOut, topOut, rightOut - leftOut, bottomOut - topOut); // Create the reduced Rect so after performing the local threshold, we can reduce the size back to the original ROI dimensions. reducedBox.Initialize(Math.Max(boundingBox.Left - leftOut, 0), Math.Max(boundingBox.Top - topOut, 0), boundingBox.Width + Math.Min(boundingBox.Left, 0), boundingBox.Height + Math.Min(boundingBox.Top, 0)); // Set this flag so the image can be reduced after performing the local threshold. useExpandedROI = 1; } } // if Expanded Box hasn't been updated, use the boundingBox passed in to extract. if (useExpandedROI == 0) { expandedBox = boundingBox; } // Extract the region of interest into the mask image. Algorithms.Extract(image, imageMask, expandedBox, 1, 1); // Create a temporary ROI that will be used to mask the extracted image, to get rid of // the pixels outside of the rotated rectangle. Roi tmpROI = new Roi(roi); // If the ROI is a rotated rectangle, then compute the new location of the search ROI. if ((roi[0].Type == ContourType.RotatedRectangle) && (((RotatedRectangleContour)roi[0].Shape).Angle > 0.01)) { CoordinateSystem baseSystem = new CoordinateSystem(); baseSystem.Origin.X = (roi.GetBoundingRectangle().Left < 0 ? 0 : roi.GetBoundingRectangle().Left); baseSystem.Origin.Y = (roi.GetBoundingRectangle().Top < 0 ? 0 : roi.GetBoundingRectangle().Top); baseSystem.Angle = 0; baseSystem.AxisOrientation = AxisOrientation.Direct; CoordinateSystem newSystem = new CoordinateSystem(new PointContour(0, 0), 0, AxisOrientation.Direct); CoordinateTransform transform = new CoordinateTransform(baseSystem, newSystem); Algorithms.TransformRoi(tmpROI, transform); } // Create a temporary image. using (VisionImage tmpImageMask = new VisionImage(ImageType.U8, 7)) { double thresholdMin; double thresholdMax; switch (preprocessingOptions.ThresholdType) { case ThresholdType.Manual: thresholdMin = preprocessingOptions.ManualThresholdRange.Minimum; thresholdMax = preprocessingOptions.ManualThresholdRange.Maximum; Algorithms.Threshold(imageMask, imageMask, new Range(thresholdMin, thresholdMax), true, 1); break; case ThresholdType.Auto: Collection <ThresholdData> thresholdData; thresholdData = Algorithms.AutoThreshold(image, tmpImageMask, 2, preprocessingOptions.AutoThresholdOptions.Method); if (preprocessingOptions.AutoThresholdOptions.ParticleType == ParticleType.Bright) { thresholdMin = (thresholdData[0].Range.Maximum > preprocessingOptions.AutoThresholdOptions.Limits.Minimum ? thresholdData[0].Range.Maximum : preprocessingOptions.AutoThresholdOptions.Limits.Minimum); thresholdMax = 255; } else { thresholdMin = 0; thresholdMax = (thresholdData[0].Range.Maximum < preprocessingOptions.AutoThresholdOptions.Limits.Maximum ? thresholdData[0].Range.Maximum : preprocessingOptions.AutoThresholdOptions.Limits.Maximum); } Algorithms.Threshold(imageMask, imageMask, new Range(thresholdMin, thresholdMax), true, 1); break; case ThresholdType.Local: LocalThresholdOptions Options = new LocalThresholdOptions(preprocessingOptions.LocalThresholdOptions.ParticleType, preprocessingOptions.LocalThresholdOptions.Method, 1.0, preprocessingOptions.LocalThresholdOptions.WindowWidth, preprocessingOptions.LocalThresholdOptions.WindowHeight); Options.DeviationWeight = preprocessingOptions.LocalThresholdOptions.DeviationWeight; Algorithms.LocalThreshold(imageMask, imageMask, Options); break; default: break; } /// If the expanded ROI was used, reduce it so no particles are found outside requested ROI. if (useExpandedROI == 1) { Algorithms.Extract(imageMask, imageMask, reducedBox, 1, 1); } // Cast the image to 8 bit. imageMask.Type = ImageType.U8; // Eliminates particles that touch the border of the image. if (preprocessingOptions.RejectBorder) { if ((roi[0].Type == ContourType.RotatedRectangle) && (((RotatedRectangleContour)roi[0].Shape).Angle > 0.01)) { // Special case for the rotated rectangle. Algorithms.Label(imageMask, imageMask, Connectivity.Connectivity8); Collection <short> lookupTable = new Collection <short>(); lookupTable.Add(0); for (int i = 1; i < 256; i++) { lookupTable.Add(1); } RoiProfileReport roiProfile = Algorithms.RoiProfile(imageMask, tmpROI); for (int i = 0; i < roiProfile.Report.ProfileData.Count; i++) { lookupTable[0] = (short)roiProfile.Report.ProfileData[i]; } Algorithms.UserLookup(imageMask, imageMask, lookupTable); } else { Algorithms.RejectBorder(imageMask, imageMask, Connectivity.Connectivity8); } } // Remove small particles. if (preprocessingOptions.NumberOfErosions > 0) { Algorithms.RemoveParticle(imageMask, imageMask, preprocessingOptions.NumberOfErosions, SizeToKeep.KeepLarge); } // If the rectangle is rotated, mask out the areas of the image that are not in the ROI. if ((roi[0].Type == ContourType.RotatedRectangle) && (((RotatedRectangleContour)roi[0].Shape).Angle > 0.01)) { // Perform the mask Algorithms.RoiToMask(tmpImageMask, tmpROI, new PixelValue(255)); Algorithms.And(imageMask, tmpImageMask, imageMask); } // Sets the mask offset. imageMask.MaskOffset.X = Math.Max(0, roi.GetBoundingRectangle().Left); imageMask.MaskOffset.Y = Math.Max(0, roi.GetBoundingRectangle().Top); } tmpROI.Dispose(); }