/// <summary> /// Run the algorithm over the image. /// </summary> /// <remarks> /// Tip: You can copy the input image, use the original data, write /// to the copy and at the end replace the original with the copy. /// </remarks> /// <param name="image">Image - both input and output</param> public override void run(Image image) { if ((ThresholdFilter == null) || (ScanningOrder == null)) { return; // TODO: throw an exception } Image.ImageRunInfo imageRunInfo = new Image.ImageRunInfo() { ScanOrder = ScanningOrder, Height = image.Height, Width = image.Width }; init(imageRunInfo); Image.IterFuncSrcDest pixelFunc; if (ErrorFilterEnabled) { // error diffusion enabled pixelFunc = ((pixel) => { double error = ErrorFilter.getError(); double original = (double)pixel[0] + error; Pixel quantized = ThresholdFilter.quantize(original, pixel.X, pixel.Y); ErrorFilter.setError(original - (double)quantized[0], pixel[0]); ErrorFilter.moveNext(); return quantized; }); } else { // error diffusion disabled pixelFunc = ((pixel) => ThresholdFilter.quantize(pixel)); } image.IterateSrcDestDirect(pixelFunc, ScanningOrder); }
public override void run(Image image) { Image.ImageRunInfo imageRunInfo = new Image.ImageRunInfo() { ScanOrder = ScanningOrder, Height = image.Height, Width = image.Width }; init(imageRunInfo); // Size of the current cluster int currentCellSize = MaxCellSize; // Index of current pixel in the current cell int currentCellPixel = 1; // 1..currentCellSize // Cell with the least brightness in the current cluster, // center of the filled part of cluster will be positioned onto it int darkestCellPixel = 1; // 1..currentCellSize // Intensity of the darkestCellPixel double minCellIntensity = 255; // TODO: max pixel value double totalCellIntensity = 0; Pixel blackPixel = new Pixel(new byte[1] { 0 }); Pixel whitePixel = new Pixel(new byte[1] { 255 }); // Pixels in the current cell waiting being filled (or not). // X: cellPixels[*, 0], Y: cellPixels[*, 1] int[,] cellPixels = new int[MaxCellSize, 2]; int clusterSize = 0; int clusterStartPos = 0; int visitedPixels = 0; int totalPixels = image.Height * image.Width; //// adaptive clustering - computing amount of local detail //int[,] sobelMatrix = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}}; int intensity = 0; int previousIntensity = 0; double error = 0.0; // current quantization error // accelerator: double maxCellSizeLog2 = Math.Log(MaxCellSize) / Math.Log(2); IEnumerator<Coordinate<int>> scanOrderEnum = ScanningOrder.getCoordsEnumerable().GetEnumerator(); image.initBuffer(); while (scanOrderEnum.MoveNext()) { int x, y; // current coordinates x = scanOrderEnum.Current.X; y = scanOrderEnum.Current.Y; visitedPixels++; // collect intensities and coordinates of cell's pixels Pixel pixel = image.getPixel(x, y); // for computing path direction vector previousIntensity = intensity; intensity = pixel[0]; double intensityWithError = intensity; if (ErrorFilterEnabled) { intensityWithError += ErrorFilter.getError(); } totalCellIntensity += intensityWithError; if (intensityWithError < minCellIntensity) { minCellIntensity = intensityWithError; darkestCellPixel = currentCellPixel; } // adaptive clustering if (UseAdaptiveClustering) { // Compute (approximated) gradient for current pixel. double derivative = 0.0; // difference with the previous pixel derivative = (intensity - previousIntensity) / 255.0; // Compute maximum allowed cell size for this pixel. // Adjust currentCellSize according to computed value. int maxAllowedClusterSize = Math.Max(Math.Min((int)( Math.Pow(2, (1 - Math.Abs(derivative)) * maxCellSizeLog2) ), MaxCellSize), MinCellSize); currentCellSize = Math.Max(maxAllowedClusterSize, currentCellPixel); } cellPixels[currentCellPixel - 1, 0] = x; cellPixels[currentCellPixel - 1, 1] = y; // What about if the cell is not completed? // - don't overwrite previously filled pixels remaining in the cellPixels array if ((currentCellPixel < currentCellSize) && (visitedPixels < totalPixels)) // TODO: ... && order.isNext() { // walk the cell currentCellPixel++; } else { // cell is walked, fill the proper ratio of cells double whitePixelsRatio = totalCellIntensity / 255.0; int whitePixelsNumber = (int)Math.Truncate(whitePixelsRatio + 0.5); //int whitePixelsNumber = (int)whitePixelsRatio; error = (whitePixelsRatio - whitePixelsNumber) * 255.0; clusterSize = currentCellSize - whitePixelsNumber; if (UseClusterPositioning) { // move the cluster - position its center to the darkestCellPixel // compute the starting position // THINK: try to add a bit of randomness inside here clusterStartPos = darkestCellPixel - (int)(clusterSize / 2.0); // correct the position if it exceeds the cell clusterStartPos = Math.Max(clusterStartPos, 0); clusterStartPos = Math.Min(clusterStartPos, currentCellSize - clusterSize); } for (int i = 0; i < clusterStartPos; i++) { image.setPixel(cellPixels[i, 0], cellPixels[i, 1], whitePixel); } for (int i = clusterStartPos; i < clusterSize + clusterStartPos; i++) { // Fill a cluster proportional to cell's average intensity with black. image.setPixel(cellPixels[i, 0], cellPixels[i, 1], blackPixel); } for (int i = clusterSize + clusterStartPos; i < currentCellSize; i++) { image.setPixel(cellPixels[i, 0], cellPixels[i, 1], whitePixel); } // start a new cell currentCellPixel = 1; darkestCellPixel = 1; minCellIntensity = 255; totalCellIntensity = 0; if (ErrorFilterEnabled) { ErrorFilter.setError(error, 0); } currentCellSize = MaxCellSize; } if (ErrorFilterEnabled) { ErrorFilter.moveNext(); } } image.flushBuffer(); }