<commit_before>/**
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * @author <NAME>
 */
package mpicbg.imglib.algorithm.floydsteinberg;

import java.util.Random;

import mpicbg.imglib.algorithm.OutputAlgorithm;
import mpicbg.imglib.algorithm.math.MathLib;
import mpicbg.imglib.container.array.ArrayContainerFactory;
import mpicbg.imglib.cursor.LocalizableByDimCursor;
import mpicbg.imglib.cursor.LocalizableCursor;
import mpicbg.imglib.cursor.array.ArrayLocalizableCursor;
import mpicbg.imglib.image.Image;
import mpicbg.imglib.image.ImageFactory;
import mpicbg.imglib.outside.OutsideStrategyValueFactory;
import mpicbg.imglib.type.NumericType;
import mpicbg.imglib.type.label.FakeType;
import mpicbg.imglib.type.logic.BitType;
import mpicbg.imglib.type.numeric.FloatType;

public class FloydSteinbergDithering<T extends NumericType<T>> implements OutputAlgorithm<BitType>
{
	Image<BitType> result;
	final Image<T> img;
	final Image<FloatType> errorDiffusionKernel;
	final int[] dim, tmp1, tmp2;
	final int numDimensions;
	final float ditheringThreshold, minValue, maxValue;
	
	String errorMessage = "";
	
	public FloydSteinbergDithering( final Image<T> img, final float ditheringThreshold )
	{
		this.img = img.clone();
		this.dim = img.getDimensions();
		this.tmp1 = img.createPositionArray();
		this.tmp2 = img.createPositionArray();

		this.errorDiffusionKernel = createErrorDiffusionKernel( img.getNumDimensions() );

		this.ditheringThreshold = ditheringThreshold;
		img.getDisplay().setMinMax();		
		this.minValue = (float)img.getDisplay().getMin();
		this.maxValue = (float)img.getDisplay().getMax();
		this.numDimensions = img.getNumDimensions();
	}

	public FloydSteinbergDithering( final Image<T> img )
	{
		this ( img, getThreshold( img ) );	
	}	

	@Override
	public boolean process()
	{		
		// creates the output image of BitType using the same Storage Strategy as the input image 
		final ImageFactory<BitType> imgFactory = new ImageFactory<BitType>( new BitType(), img.getStorageFactory() );
		result = imgFactory.createImage( dim );
		
		// we create a Cursor that traverses (top -> bottom) and (left -> right) in n dimensions,
		// which is a Cursor on a normal Array, therefore we use a FakeArray which just gives us position
		// information without allocating memory
		final LocalizableCursor<FakeType> cursor = ArrayLocalizableCursor.createLinearCursor( dim );

		// we also need a Cursors for the input, the output and the kernel image
		final LocalizableByDimCursor<T> cursorInput = img.createLocalizableByDimCursor( new OutsideStrategyValueFactory<T>() );
		final LocalizableByDimCursor<BitType> cursorOutput = result.createLocalizableByDimCursor();
		final LocalizableCursor<FloatType> cursorKernel = errorDiffusionKernel.createLocalizableCursor();
		
		while( cursor.hasNext() )
		{
			cursor.fwd();
			
			// move input and output cursor to the current location
			cursorInput.moveTo( cursor );
			cursorOutput.moveTo( cursor );
			
			// set new value and compute error
			final float error;
			final float in = cursorInput.getType().getReal(); 
			if ( in < ditheringThreshold )
			{
				cursorOutput.getType().setZero();
				error = in - minValue; 
			}
			else
			{
				cursorOutput.getType().setOne();
				error = in - maxValue; 
			}

			// distribute the error
			cursorKernel.reset();
			cursor.getPosition( tmp1 );
			
			while ( cursorKernel.hasNext() )
			{
				cursorKernel.fwd();				
				
				final float value = error * cursorKernel.getType().get();
				cursorKernel.getPosition( tmp2 );
				
				for ( int d = 0; d < numDimensions; ++d )
					tmp2[ d ] += tmp1[ d ] - 1;
				
				cursorInput.moveTo( tmp2 );
				cursorInput.getType().setReal( cursorInput.getType().getReal() + value );
			}
			
		}
		
		// close all cursors
		cursor.close();
		cursorInput.close();
		cursorOutput.close();
		cursorKernel.close();

		// close image
		img.close();<commit_msg>Speeded up processing as suggested by Aivar<commit_after>/**
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * @author <NAME>
 */
package mpicbg.imglib.algorithm.floydsteinberg;

import java.util.Random;

import mpicbg.imglib.algorithm.OutputAlgorithm;
import mpicbg.imglib.algorithm.math.MathLib;
import mpicbg.imglib.container.array.ArrayContainerFactory;
import mpicbg.imglib.cursor.LocalizableByDimCursor;
import mpicbg.imglib.cursor.LocalizableCursor;
import mpicbg.imglib.cursor.array.ArrayLocalizableCursor;
import mpicbg.imglib.image.Image;
import mpicbg.imglib.image.ImageFactory;
import mpicbg.imglib.outside.OutsideStrategyValueFactory;
import mpicbg.imglib.type.NumericType;
import mpicbg.imglib.type.label.FakeType;
import mpicbg.imglib.type.logic.BitType;
import mpicbg.imglib.type.numeric.FloatType;

public class FloydSteinbergDithering<T extends NumericType<T>> implements OutputAlgorithm<BitType>
{
	Image<BitType> result;
	final Image<T> img;
	final Image<FloatType> errorDiffusionKernel;
	final int[] dim, tmp1, tmp2;
	final int numDimensions;
	final float ditheringThreshold, minValue, maxValue;
	
	String errorMessage = "";
	
	public FloydSteinbergDithering( final Image<T> img, final float ditheringThreshold )
	{
		this.img = img.clone();
		this.dim = img.getDimensions();
		this.tmp1 = img.createPositionArray();
		this.tmp2 = img.createPositionArray();

		this.errorDiffusionKernel = createErrorDiffusionKernel( img.getNumDimensions() );

		this.ditheringThreshold = ditheringThreshold;
		img.getDisplay().setMinMax();		
		this.minValue = (float)img.getDisplay().getMin();
		this.maxValue = (float)img.getDisplay().getMax();
		this.numDimensions = img.getNumDimensions();
	}

	public FloydSteinbergDithering( final Image<T> img )
	{
		this ( img, getThreshold( img ) );	
	}	

	@Override
	public boolean process()
	{		
		// creates the output image of BitType using the same Storage Strategy as the input image 
		final ImageFactory<BitType> imgFactory = new ImageFactory<BitType>( new BitType(), img.getStorageFactory() );
		result = imgFactory.createImage( dim );
		
		// we create a Cursor that traverses (top -> bottom) and (left -> right) in n dimensions,
		// which is a Cursor on a normal Array, therefore we use a FakeArray which just gives us position
		// information without allocating memory
		final LocalizableCursor<FakeType> cursor = ArrayLocalizableCursor.createLinearCursor( dim );

		// we also need a Cursors for the input, the output and the kernel image
		final LocalizableByDimCursor<T> cursorInput = img.createLocalizableByDimCursor( new OutsideStrategyValueFactory<T>() );
		final LocalizableByDimCursor<BitType> cursorOutput = result.createLocalizableByDimCursor();
		final LocalizableCursor<FloatType> cursorKernel = errorDiffusionKernel.createLocalizableCursor();
		
		while( cursor.hasNext() )
		{
			cursor.fwd();
			
			// move input and output cursor to the current location
			cursorInput.moveTo( cursor );
			cursorOutput.moveTo( cursor );
			
			// set new value and compute error
			final float error;
			final float in = cursorInput.getType().getReal(); 
			if ( in < ditheringThreshold )
			{
				cursorOutput.getType().setZero();
				error = in - minValue; 
			}
			else
			{
				cursorOutput.getType().setOne();
				error = in - maxValue; 
			}
			
			if ( error != 0.0f )
			{
				// distribute the error
				cursorKernel.reset();
				cursorKernel.fwd( errorDiffusionKernel.getNumPixels()/2 );
				cursor.getPosition( tmp1 );			
				
				while ( cursorKernel.hasNext() )
				{
					cursorKernel.fwd();				
					
					final float value = error * cursorKernel.getType().get();
					cursorKernel.getPosition( tmp2 );
					
					for ( int d = 0; d < numDimensions; ++d )
						tmp2[ d ] += tmp1[ d ] - 1;
					
					cursorInput.moveTo( tmp2 );
					cursorInput.getType().setReal( cursorInput.getType().getReal() + value );
				}
			}		
		}
		
		// close all cursors
		cursor.close();
		cursorInput.close();
		cursorOutput.close();
		cursorKernel.close();

		// close image
		img.close();
{'fraction_non_alphanumeric': 0.10069975945768642, 'fraction_numerical': 0.006013557839492674, 'mean_word_length': 3.998360655737705, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3316229', 'n_tokens_mistral': 3093, 'n_tokens_neox': 2717, 'n_words': 778}