Giter Club home page Giter Club logo

imglib2-algorithm's Introduction

Image.sc Forum developer chat

ImgLib2 is a general-purpose, multidimensional image processing library.

It provides an interface-driven design that supports numeric and non-numeric data types (8-bit unsigned integer, 32-bit floating point, etc.) in an extensible way. It implements several data sources and sample organizations, including one single primitive array, one array per plane, N-dimensional array "cells" cached to and from disk on demand, and planes read on demand from disk.

Benefits

  1. By avoiding unnecessarily complex syntax (such as nested loops) ImgLib2 allows developers to concentrate on the essence of the algorithm.

  2. By being conciser, ImgLib2 makes it much harder to write buggy code.

  3. ImgLib2 is dimension-independent. That means that you usually express your code in a way that can be applied to 2-, 3- or even 100-dimensional data.

  4. ImgLib2 has no limit on channels. You can have a fine-grained spectrum for every single pixel, if your hardware allows for that.

  5. ImgLib2 is actually not limited to images; e.g., we have examples working on RNA sequences.

  6. ImgLib2 provides transparent data access. The algorithm does not need to know that it is working on a virtual stack, and the data can actually be generated on the fly. Think about a fractal and being able to zoom in indefinitely; this is an image that you can use with any ImgLib algorithm.

  7. ImgLib2 makes it an ultra-cheap operation to work on sections of images. There is no need to copy data around.

  8. ImgLib2 is so self-contained that it could serve as the underlying data handling library for every Java-based project.

Applications

Resources

Building the source code

You can build the source from the command line using Maven:

mvn

You can also import the source into Eclipse using the m2e plugin. Download Eclipse IDE for Java Developers (3.7 Indigo or later), which comes with m2e preinstalled. Then run:

File > Import > Existing Maven Projects

Select the toplevel folder of your ImgLib working copy, and Eclipse will find all the ImgLib projects.

Both NetBeans and IntelliJ IDEA also have built-in support for Maven projects.

ImgLib1

The previous incarnation of the library, known as ImgLib1, is still available as part of Fiji. However, we strongly encourage developers to use ImgLib2 instead, and migrate existing ImgLib1 programs to ImgLib2 whenever possible.

imglib2-algorithm's People

Contributors

acardona avatar axtimwalde avatar bdezonia avatar bogovicj avatar ctrueden avatar dietzc avatar dscho avatar gselzer avatar hanslovsky avatar hinerm avatar jdeschamps avatar maarzt avatar panovr avatar squareys avatar stelfrich avatar stephanpreibisch avatar tibuch avatar tinevez avatar tpietzsch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imglib2-algorithm's Issues

Unable to use CellImg as input for some algorithms

Hi,

I am trying to write some simple code to try out imglib2, basically opening an image, process it using one of the algorithms and save it.
When I want to open the image as a CellImg, I then get the following error when I call one of the Gauss or Thresholder for example:

Exception in thread "main" java.lang.IllegalStateException: Tried to create a new SCIFIOCellImg without a Reader to use for opening planes.
Call setReader(Reader) before invoking create()
	at io.scif.img.cell.SCIFIOCellImgFactory.create(SCIFIOCellImgFactory.java:159)
	at io.scif.img.cell.SCIFIOCellImgFactory.create(SCIFIOCellImgFactory.java:71)
	at net.imglib2.img.ImgFactory.create(ImgFactory.java:74)
	at net.imglib2.algorithm.gauss.AbstractGauss.<init>(AbstractGauss.java:110)
	at net.imglib2.algorithm.gauss.GaussFloat.<init>(GaussFloat.java:110)
	at net.imglib2.algorithm.gauss.Gauss.inFloatInPlace(Gauss.java:313)
	at net.imglib2.algorithm.gauss.Gauss.inFloatInPlace(Gauss.java:271)
	at net.imglib2.algorithm.gauss.Gauss.inFloatInPlace(Gauss.java:256)

Here is the code producing the error:

File file = new File( "/path/to/input/DrosophilaWing.tif" );  
String path = file.getAbsolutePath();

File fileO = new File( "/path/to/output/DrosophilaWing.tif" );
			
SCIFIOConfig config = new SCIFIOConfig();
config.imgOpenerSetImgModes( ImgMode.CELL );
			
ImgOpener imgOpener = new ImgOpener();

System.out.println("Opening input image");
final Img< T > image = (Img<T>) imgOpener.openImg( path, config );
			
System.out.println(image.factory().getClass());

System.out.println("Convolve image");
Gauss.inFloatInPlace( 2, image);
			
System.out.println("Save image");
ImgSaver imgSaver = new ImgSaver();
imgSaver.saveImg(fileO.getAbsolutePath(), image);

Am I missing something? If I don't put the SCIFIOConfig when opening the image, it is opened as an ArrayImg and I don't get any error.

Thanks!

Histogram RealBinMapper - bin centers inconsistent with given min / max values

Bin centers computed by net.imglib2.algorithm.stats.RealBinMapper are not consistent with the input minBin and maxBin values and not what I'd expect.

invMap's behavior is also not what I'd expect:

  • Put 4 bins between the interval [0.0 1.0]
    • Expect bin "boundaries" at 1/4, 2/4, 3/4 and therefore:
    • Expect bin centers at 1/8, 3/8, 5/8, 7/8
    • Observe: centers are not what was expected, some are out-of-bounds

Pseudocode:

mapper = RealBinMapper( 0.0, 1.0, 4 )  

println( mapper.invMap( 0 ))
println( mapper.invMap( 1 ))
println( mapper.invMap( 2 ))
println( mapper.invMap( 3 ))

outputs

0.0
0.5
1.0
1.5

net.imglib2.histogram.Real1dBinMapper however seems to work as expected.
https://github.com/imglib/imglib2/tree/master/src/main/java/net/imglib2/histogram

Perhaps we should deprecate net.imglib2.algorithm.stats in favor of net.imglib2.histogram (?)
which so far seems to have the behavior I expect.

Merge fixDependencies branch as soon as possible and re-distribute JAR's

Hi @tpietzsch @axtimwalde @ctrueden,

we discussed the same problem some time ago. The current version of imglib2-algorithm is compiled against imglib2-3.3.0 and not the current 4.6.0.

This results in the problem that any algorithm that makes a CellImg crashes since the CellImg constructor changed from imglib2-3.. to imglib2-4.. (for example Gauss3 on images > 2^31 pixels).

This is very critical for our applications. We should just merge the branch containing my "fix" 0f6c1fd

and re-distribute ASAP.

Cheers,
Stephan

Potential Bug in DiamondShape

I recently had an issue in a rather complex scenario including Views.collapse, Views.hyperSlice, DiamondShape.neighborhoodsRandomAccessible and some other view operations.
Starting from a 4D RAI, I used Views.collapse to collapse the last dimension and iterated over all positions of the last axis of the resulting 3D image, fixing the last dimension to each position pos using Views.hyperSlice. I then ran some operations on those 2D hyperslices using neighborhoods defined by DiamondShape. For each pos, instead of looking at the slice at pos, the code ran on the slice at 0.

I now managed to create a minimum working example for this (complete code):

public static void main( final String[] args )
{
	final long[] dims = { 3, 3, 2, 1 };
	final ArrayImg< FloatType, FloatArray > imgs = ArrayImgs.floats( dims );
	for ( int z = 0; z < dims[ 2 ]; ++z )
		for ( final FloatType h : Views.hyperSlice( imgs, 2, z ) )
			h.set( z );

	final boolean logPosition = false;
	final CompositeIntervalView< FloatType, RealComposite< FloatType > > collapsed = Views.collapseReal( logPosition ? Views.interval( log( imgs ), imgs ) : imgs );

	final int sliceAxis = 2;
	final long slicePos = 1;

	final RandomAccessibleInterval< RealComposite< FloatType > > hs1 = Views.hyperSlice( collapsed, sliceAxis, slicePos );


	final boolean useDiamondShape = true;
	final Shape shape = useDiamondShape ? new DiamondShape( 1 ) : new RectangleShape( 1, false );
	final RandomAccessible< Neighborhood< RealComposite< FloatType > > > nh1 = shape.neighborhoodsRandomAccessible( hs1 );
	final RandomAccessible< Neighborhood< RealComposite< FloatType > > > nh2 = shape.neighborhoodsRandomAccessible( Views.extendValue( hs1, Views.collapseReal( ArrayImgs.floats( 1, dims[ 3 ] ) ).randomAccess().get() ) );

	final long[] xyPosition = { 1, 1 };

	System.out.println( "Outputs should be the same ([1.0] at every position) with or without extension." );

	System.out.println( "Without extension: " );
	iterateAndPrintComposite( nh1.randomAccess(), xyPosition, ( int ) dims[ 3 ] );

	System.out.println( "With extension: " );
	iterateAndPrintComposite( nh2.randomAccess(), xyPosition, ( int ) dims[ 3 ] );
}

The code is self-contained except for imglib2 and imglib2-algorithm dependencies and helper methods log (log the position of the RandomAccess on every call to get) and iterateAndPrintComposite (iterate over all Cursor elements and print the contained Composite as well as current position) which are available in the complete example.

This is what the code does:

  • imgs <- Create a 4D RandomAccessibleInterval. Each pixel value is set to the pixel's z-index.
  • collapsed <- Collapse last dimension of imgs
  • hs1 <- Fix last (i.e. third dimension or z) of collapsed on position 1.
  • nh1 <- Create a RandomAccessible< Neighborhood > on hs1
  • nh2 <- Create a RandomAccessible< Neighborhood > on an extended view of hs1
  • Set accesses to nh1 and nh2 to center pixel ([1, 1])
  • Iterate over neighborhood for both accesses and print values and position.

I added two parameters for test purposes:

  • logPosition if true: Print the position of the imgs RandomAccess on every get.
  • useDiamondShape if true: Use DiamondShape as neighborhood, RectangleShape otherwise.

In any case, the expected output would be

...
[1.0] (x,y)
...

regardless of the neighborhood or extension. This is true for RectangleShape:

Outputs should be the same ([1.0] at every position) with or without extension.
Without extension: 
[1.0] (0,0)
[1.0] (1,0)
[1.0] (2,0)
[1.0] (0,1)
[1.0] (1,1)
[1.0] (2,1)
[1.0] (0,2)
[1.0] (1,2)
[1.0] (2,2)
With extension: 
[1.0] (0,0)
[1.0] (1,0)
[1.0] (2,0)
[1.0] (0,1)
[1.0] (1,1)
[1.0] (2,1)
[1.0] (0,2)
[1.0] (1,2)
[1.0] (2,2)

When I use DiamondShape, however, the output is wrong for the non-extended RandomAccessibleInterval:

Outputs should be the same ([1.0] at every position) with or without extension.
Without extension: 
[0.0] (1,0)
[0.0] (0,1)
[0.0] (1,1)
[0.0] (2,1)
[0.0] (1,2)
With extension: 
[1.0] (1,0)
[1.0] (0,1)
[1.0] (1,1)
[1.0] (2,1)
[1.0] (1,2)

Additional logging (every line starting with Pos is:) of the source RandomAccess at every get shows that the RandomAccess that looks into imgs is set to 0 instead of 1 for the third dimension when no extension is used:

Outputs should be the same ([1.0] at every position) with or without extension.
Without extension: 
Pos is: (1,0,0,0)
[0.0] (1,0)
Pos is: (0,1,0,0)
[0.0] (0,1)
Pos is: (1,1,0,0)
[0.0] (1,1)
Pos is: (2,1,0,0)
[0.0] (2,1)
Pos is: (1,2,0,0)
[0.0] (1,2)
With extension: 
Pos is: (1,0,1,0)
[1.0] (1,0)
Pos is: (0,1,1,0)
[1.0] (0,1)
Pos is: (1,1,1,0)
[1.0] (1,1)
Pos is: (2,1,1,0)
[1.0] (2,1)
Pos is: (1,2,1,0)
[1.0] (1,2)

I wasn't able to find out the precise cause of this but it seems to be specific to DiamondShape in conjunction with Views.collapse and Views.hyperSlice.

SubpixelLocalization class biased positions for 3D images.

This bug is responsible for the TrackMate bug trackmate-sc/TrackMate#68.

The SubpixelLocalization class does not work 'fairly' in 3D. The Y coordinates is off by a considerable factor when localizaing peaks, whereas the X dimension is not affected.

Here are some results on synthetic images. Below I vary the peak position in X from 0 to 1 with 0.1 increments. the Y position of the peak is fixed to be 0.4 + integer.

subpixel_loc_error_dy0 4_dz0

The problem is not seen in 2D.

Deprecate/Remove one of HyperSphere or HyperSphereNeighborhood

HyperSphere and HyperSphereNeighborhood do the same thing. Having both in imglib2-algorithm is confusing for the caller. I suggest to remove HyperSphere once #67 is fixed because HyperSphereNeighborhood implements Neighborhood as a common interface with other similar classes (HyperSphere is the lone class in its package).

ExecutorService exception handling

In several places we have code like this:

for ( final Future< Void > f : futures )
{
	try
	{
		f.get();
	}
	catch ( final InterruptedException e )
	{
		e.printStackTrace();
	}
	catch ( final ExecutionException e )
	{
		e.printStackTrace();
	}
}

This should be changed from e.printStackTrace() to something meaningful.

InterruptedException should Thread.currentThread().interrupt() and end method as fast as possible.

What should we do with ExecutionException? Just declare and throw it?

FloodFill interface is not intuitive

Two of the parameters of FloodFill.fill are not intuitive:

  • Filter< Pair< T, U >, Pair< T, U > > filter: Compares a pair of image and mask pixels with a reference pair to determine whether or not the pixel should be added to the floodfill. A BiPredicate< T, U > that only checks the current pixel is more intuitive and the caller will always know the reference values anyway, and can include them in the BiPredicate if necessary.
  • Writer< U > writer: Interface that defines how to update a target value with a source value. Again, the caller has access to the source value and it would thus be more intuitive to use a Consumer< U > in this case.

I suggest we deprecate the Filter and Writer interfaces, and all method signatures that use these interfaces. We add new signatures that take BiPredicate and Consumer instead, and convenience methods as appiclable.

Shape Naming and Non-Centered Rectangles

Hi everybody!

I noticed the following naming inconsistency:

  • RectangleShape has a span in every direction and is therefore a hypercube and centered
  • CenteredRectangleShape has no non-centered counterpart.

In general, there are no non-centered RectangleShapes and RectangleShape could be easily implemented over a fitting span parameter for CenteredRectangleShape.

This will probably be hard to do because of API breaks, but I would suggest the following:

Rename CenteredRectangleShape to (Hyper)RectangleShape and make it uncentered, then do the centering and the Cube shapes over constructor parameters or subclasses (Hyper)CubeShape and Centered*Shape.

As far as I can tell, the current implementation of RectangleNeighborhood (aka. CenteredRectangleNeighborhood) is flexible enough to be easily adapted to an uncentered implementation.
Benefits: Straight-forward naming and code reuse for all Rectangle-based shapes.

Please tell me what you think and if I missed something.
Greetings, Squareys.

HyperEllipsoid

Together with @hanslovsky, we added HyperEllipsoidNeighborhood and factory eaa8e16. Missing is sensible integration and proper code sharing with HyperSphereNeighborhood and its outer access patterns. Who should extend whom and what abstract classes should we make? Comments and thoughts very welcome, @tpietzsch ?

Issues with HyperSphereNeighborhood

(1) The class is missing documentation.
(2) The factory method has a single method create that takes a long[] for the position. It would be nice if a second create method took a Localizable (from which a long[] can be extracted) or at least a Point.

Problems with watershedding (deprecated version)

Hey,

I tried using watershedding implementation of imglib2, my attempt is shown below. The problem here is that it does not do watershedding, I checked with the plugin in Fiji and it works fine for the same image. The problem seems to be not in the displaying of watershedded regions but in the input image set up for the watershed object. Any ideas why my implementation does not work?

******************* Using watershedding implementation of Imglib2
@SuppressWarnings("deprecation")
public static void OldWatersherImage(RandomAccessibleInterval inputimg,
RandomAccessibleInterval seedimg, int background) {

    int n = inputimg.numDimensions();
    long[] dimensions = new long[n];
    long[] positionseed = new long[n];

    for (int d = 0; d < n; ++d)
        dimensions[d] = inputimg.dimension(d);

    final NativeImgLabeling<Integer, ShortType> outputLabeling = new NativeImgLabeling<Integer, ShortType>(
            new ArrayImgFactory<ShortType>().create(inputimg, new ShortType()));

    final NativeImgLabeling<Integer, ShortType> seedLabeling = new NativeImgLabeling<Integer, ShortType>(
            new ArrayImgFactory<ShortType>().create(seedimg, new ShortType()));

    final Cursor<LabelingType<Integer>> seedlabelcursor = seedLabeling.localizingCursor();

    ImageJFunctions.show(inputimg).setTitle("Input image for watershedding");
    ImageJFunctions.show(seedimg).setTitle("Seed image for watershedding");

    final RandomAccess<FloatType> seedcursor = seedimg.randomAccess();

    // Fill the seedlabel image

    while (seedlabelcursor.hasNext()) {
        LabelingType<Integer> seedl = seedlabelcursor.next();
        seedlabelcursor.localize(positionseed);
        seedcursor.setPosition(positionseed);
        int seedLabel = (int) seedcursor.get().get();
        if (seedLabel == background)
            continue;
        seedl.setLabel(seedLabel);

    }

    final Watershed<FloatType, Integer> watershed = new Watershed<FloatType, Integer>();
    watershed.process();

    watershed.setSeeds(seedLabeling);
    watershed.setIntensityImage(inputimg);
    watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2));
    watershed.setOutputLabeling(outputLabeling);
    watershed.getResult();
    RandomAccessibleInterval<FloatType> outimg = new ArrayImgFactory<FloatType>().create(inputimg, new FloatType());
    Cursor<LabelingType<Integer>> labelCursor = outputLabeling.cursor();
    RandomAccess<FloatType> imageRA = outimg.randomAccess();

    // Go through the whole image again and again for every single label,
    // until no more label is found.
    ( To display the watershedded regions)
            int currentLabel = 0;
    boolean anythingFound = true;
    while (anythingFound) {
        anythingFound = false;
        labelCursor.reset();

        // Go through the whole image and add every pixel, that belongs to
        // the currently processed label
        int count = 0;
        while (labelCursor.hasNext()) {
            labelCursor.fwd();
            imageRA.setPosition(labelCursor);

            int i = (int) (imageRA.get().get());
            if (i == currentLabel) {
                anythingFound = true;
                count++;
            }
        }
        System.out.println("Number of input pixels in label " + currentLabel + ": " + count);
        currentLabel++;
        ImageJFunctions.show(outimg).setTitle("Watershed Images");
    }

Potential bug in HyperSphereNeighborhood when used with RealViews.affine and Views.hyperSlice

In this MWE (depending on imglib2-algorithm-0.9.0)

import java.util.Arrays;

import net.imglib2.Cursor;
import net.imglib2.Point;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccessible;
import net.imglib2.algorithm.neighborhood.HyperSphereNeighborhood;
import net.imglib2.algorithm.region.hypersphere.HyperSphere;
import net.imglib2.img.array.ArrayImg;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.img.basictypeaccess.array.ByteArray;
import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory;
import net.imglib2.realtransform.AffineGet;
import net.imglib2.realtransform.AffineRandomAccessible;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.realtransform.RealViews;
import net.imglib2.realtransform.Scale3D;
import net.imglib2.type.numeric.NumericType;
import net.imglib2.type.numeric.integer.UnsignedByteType;
import net.imglib2.view.ExtendedRandomAccessibleInterval;
import net.imglib2.view.Views;

public class HyperSphereBugMaybe
{

	public static void main( final String[] args )
	{

		final long dims[] = { 100, 200, 300 };
		final long radius = 15;
		final long[] pos = { 25, 50 };

		final ArrayImg< UnsignedByteType, ByteArray > img1 = ArrayImgs.unsignedBytes( dims );
		final ArrayImg< UnsignedByteType, ByteArray > img2 = ArrayImgs.unsignedBytes( dims );
		final RandomAccessible< UnsignedByteType > slice1 = makeSlice( img1 );
		final RandomAccessible< UnsignedByteType > slice2 = makeSlice( img2 );

		for ( Cursor< UnsignedByteType > c1 = Views.flatIterable( img1 ).cursor(), c2 = Views.flatIterable( img2 ).cursor(); c1.hasNext(); )
		{
			if ( !c1.next().valueEquals( c2.next() ) )
			{
				System.out.println( "ALREADY MISMATCH BEFORE ANYTHING HAPPENED!" );
			}
		}

		new HyperSphere<>( slice1, Point.wrap( pos ), radius ).forEach( UnsignedByteType::setOne );
		HyperSphereNeighborhood
				.< UnsignedByteType >factory()
				.create( pos, radius, slice2.randomAccess() )
				.forEach( UnsignedByteType::setOne );

		int unequalCount = 0;
		for ( Cursor< UnsignedByteType > c1 = Views.flatIterable( img1 ).cursor(), c2 = Views.flatIterable( img2 ).cursor(); c1.hasNext(); )
		{
			final UnsignedByteType v1 = c1.next();
			final UnsignedByteType v2 = c2.next();
			if ( !v1.valueEquals( v2 ) )
			{
				System.out.println( "v1=" + v1 + ", v2=" + v2 + ", c1=" + new Point( c1 ) + ", c2=" + new Point( c2 ) );
				++unequalCount;
			}
		}
		// unequalCount != 0
		System.out.println( "count=" + unequalCount + ", sphere size=" + new HyperSphere<>( slice1, Point.wrap( pos ), radius ).size() );

		// sanity check
		final ArrayImg< UnsignedByteType, ByteArray > img3 = ArrayImgs.unsignedBytes( dims[ 0 ], dims[ 1 ] );
		final ArrayImg< UnsignedByteType, ByteArray > img4 = ArrayImgs.unsignedBytes( dims[ 0 ], dims[ 1 ] );
		new HyperSphere<>( img3, Point.wrap( pos ), radius ).forEach( UnsignedByteType::setOne );
		HyperSphereNeighborhood
				.< UnsignedByteType >factory()
				.create( pos, radius, img4.randomAccess() )
				.forEach( UnsignedByteType::setOne );
		System.out.println( "Equal? " + Arrays.equals( img3.update( null ).getCurrentStorageArray(), img4.update( null ).getCurrentStorageArray() ) );
		int nonZeroCount = 0;
		for ( final byte b : img3.update( null ).getCurrentStorageArray() )
		{
			nonZeroCount += b == 0 ? 0 : 1;
		}
		System.out.println( nonZeroCount );
	}

	private static < T extends NumericType< T > > RandomAccessible< T > makeSlice(
			final RandomAccessibleInterval< T > rai )
	{
		final ExtendedRandomAccessibleInterval< T, ? > extended1 = Views.extendZero( rai );
		final RealRandomAccessible< T > interpolated1 = Views.interpolate( extended1, new NearestNeighborInterpolatorFactory<>() );
		final AffineTransform3D transform = new AffineTransform3D().concatenate( new Scale3D( 0.5, 0.5, 0.5 ) );
		final AffineRandomAccessible< T, AffineGet > transformed = RealViews.affine( interpolated1, transform );
		return Views.hyperSlice( transformed, 2, 75l );
	}

}

I would expect img1 and img2 to be the same after iterating and writing into them to be identical but they are not (they differ in 710 places). Note that the sanity check on two plain ArrayImgs (img3 and img4) runs successfully (no difference, and both not all zeros). Output:

count=710, sphere size=709

v1=0, v2=1, c1=(0,0,0), c2=(0,0,0)
v1=1, v2=0, c1=(50,70,150), c2=(50,70,150)
v1=1, v2=0, c1=(40,72,150), c2=(40,72,150)
v1=1, v2=0, c1=(42,72,150), c2=(42,72,150)
v1=1, v2=0, c1=(44,72,150), c2=(44,72,150)
v1=1, v2=0, c1=(46,72,150), c2=(46,72,150)
v1=1, v2=0, c1=(48,72,150), c2=(48,72,150)
v1=1, v2=0, c1=(50,72,150), c2=(50,72,150)
v1=1, v2=0, c1=(52,72,150), c2=(52,72,150)
v1=1, v2=0, c1=(54,72,150), c2=(54,72,150)
v1=1, v2=0, c1=(56,72,150), c2=(56,72,150)
v1=1, v2=0, c1=(58,72,150), c2=(58,72,150)
v1=1, v2=0, c1=(60,72,150), c2=(60,72,150)
v1=1, v2=0, c1=(36,74,150), c2=(36,74,150)
v1=1, v2=0, c1=(38,74,150), c2=(38,74,150)
v1=1, v2=0, c1=(40,74,150), c2=(40,74,150)
v1=1, v2=0, c1=(42,74,150), c2=(42,74,150)
v1=1, v2=0, c1=(44,74,150), c2=(44,74,150)
v1=1, v2=0, c1=(46,74,150), c2=(46,74,150)
v1=1, v2=0, c1=(48,74,150), c2=(48,74,150)
v1=1, v2=0, c1=(50,74,150), c2=(50,74,150)
v1=1, v2=0, c1=(52,74,150), c2=(52,74,150)
v1=1, v2=0, c1=(54,74,150), c2=(54,74,150)
v1=1, v2=0, c1=(56,74,150), c2=(56,74,150)
v1=1, v2=0, c1=(58,74,150), c2=(58,74,150)
v1=1, v2=0, c1=(60,74,150), c2=(60,74,150)
v1=1, v2=0, c1=(62,74,150), c2=(62,74,150)
v1=1, v2=0, c1=(64,74,150), c2=(64,74,150)
v1=1, v2=0, c1=(32,76,150), c2=(32,76,150)
v1=1, v2=0, c1=(34,76,150), c2=(34,76,150)
v1=1, v2=0, c1=(36,76,150), c2=(36,76,150)
v1=1, v2=0, c1=(38,76,150), c2=(38,76,150)
v1=1, v2=0, c1=(40,76,150), c2=(40,76,150)
v1=1, v2=0, c1=(42,76,150), c2=(42,76,150)
v1=1, v2=0, c1=(44,76,150), c2=(44,76,150)
v1=1, v2=0, c1=(46,76,150), c2=(46,76,150)
v1=1, v2=0, c1=(48,76,150), c2=(48,76,150)
v1=1, v2=0, c1=(50,76,150), c2=(50,76,150)
v1=1, v2=0, c1=(52,76,150), c2=(52,76,150)
v1=1, v2=0, c1=(54,76,150), c2=(54,76,150)
v1=1, v2=0, c1=(56,76,150), c2=(56,76,150)
v1=1, v2=0, c1=(58,76,150), c2=(58,76,150)
v1=1, v2=0, c1=(60,76,150), c2=(60,76,150)
v1=1, v2=0, c1=(62,76,150), c2=(62,76,150)
v1=1, v2=0, c1=(64,76,150), c2=(64,76,150)
v1=1, v2=0, c1=(66,76,150), c2=(66,76,150)
v1=1, v2=0, c1=(68,76,150), c2=(68,76,150)
v1=1, v2=0, c1=(30,78,150), c2=(30,78,150)
v1=1, v2=0, c1=(32,78,150), c2=(32,78,150)
v1=1, v2=0, c1=(34,78,150), c2=(34,78,150)
v1=1, v2=0, c1=(36,78,150), c2=(36,78,150)
v1=1, v2=0, c1=(38,78,150), c2=(38,78,150)
v1=1, v2=0, c1=(40,78,150), c2=(40,78,150)
v1=1, v2=0, c1=(42,78,150), c2=(42,78,150)
v1=1, v2=0, c1=(44,78,150), c2=(44,78,150)
v1=1, v2=0, c1=(46,78,150), c2=(46,78,150)
v1=1, v2=0, c1=(48,78,150), c2=(48,78,150)
v1=1, v2=0, c1=(50,78,150), c2=(50,78,150)
v1=1, v2=0, c1=(52,78,150), c2=(52,78,150)
v1=1, v2=0, c1=(54,78,150), c2=(54,78,150)
v1=1, v2=0, c1=(56,78,150), c2=(56,78,150)
v1=1, v2=0, c1=(58,78,150), c2=(58,78,150)
v1=1, v2=0, c1=(60,78,150), c2=(60,78,150)
v1=1, v2=0, c1=(62,78,150), c2=(62,78,150)
v1=1, v2=0, c1=(64,78,150), c2=(64,78,150)
v1=1, v2=0, c1=(66,78,150), c2=(66,78,150)
v1=1, v2=0, c1=(68,78,150), c2=(68,78,150)
v1=1, v2=0, c1=(70,78,150), c2=(70,78,150)
v1=1, v2=0, c1=(28,80,150), c2=(28,80,150)
v1=1, v2=0, c1=(30,80,150), c2=(30,80,150)
v1=1, v2=0, c1=(32,80,150), c2=(32,80,150)
v1=1, v2=0, c1=(34,80,150), c2=(34,80,150)
v1=1, v2=0, c1=(36,80,150), c2=(36,80,150)
v1=1, v2=0, c1=(38,80,150), c2=(38,80,150)
v1=1, v2=0, c1=(40,80,150), c2=(40,80,150)
v1=1, v2=0, c1=(42,80,150), c2=(42,80,150)
v1=1, v2=0, c1=(44,80,150), c2=(44,80,150)
v1=1, v2=0, c1=(46,80,150), c2=(46,80,150)
v1=1, v2=0, c1=(48,80,150), c2=(48,80,150)
v1=1, v2=0, c1=(50,80,150), c2=(50,80,150)
v1=1, v2=0, c1=(52,80,150), c2=(52,80,150)
v1=1, v2=0, c1=(54,80,150), c2=(54,80,150)
v1=1, v2=0, c1=(56,80,150), c2=(56,80,150)
v1=1, v2=0, c1=(58,80,150), c2=(58,80,150)
v1=1, v2=0, c1=(60,80,150), c2=(60,80,150)
v1=1, v2=0, c1=(62,80,150), c2=(62,80,150)
v1=1, v2=0, c1=(64,80,150), c2=(64,80,150)
v1=1, v2=0, c1=(66,80,150), c2=(66,80,150)
v1=1, v2=0, c1=(68,80,150), c2=(68,80,150)
v1=1, v2=0, c1=(70,80,150), c2=(70,80,150)
v1=1, v2=0, c1=(72,80,150), c2=(72,80,150)
v1=1, v2=0, c1=(26,82,150), c2=(26,82,150)
v1=1, v2=0, c1=(28,82,150), c2=(28,82,150)
v1=1, v2=0, c1=(30,82,150), c2=(30,82,150)
v1=1, v2=0, c1=(32,82,150), c2=(32,82,150)
v1=1, v2=0, c1=(34,82,150), c2=(34,82,150)
v1=1, v2=0, c1=(36,82,150), c2=(36,82,150)
v1=1, v2=0, c1=(38,82,150), c2=(38,82,150)
v1=1, v2=0, c1=(40,82,150), c2=(40,82,150)
v1=1, v2=0, c1=(42,82,150), c2=(42,82,150)
v1=1, v2=0, c1=(44,82,150), c2=(44,82,150)
v1=1, v2=0, c1=(46,82,150), c2=(46,82,150)
v1=1, v2=0, c1=(48,82,150), c2=(48,82,150)
v1=1, v2=0, c1=(50,82,150), c2=(50,82,150)
v1=1, v2=0, c1=(52,82,150), c2=(52,82,150)
v1=1, v2=0, c1=(54,82,150), c2=(54,82,150)
v1=1, v2=0, c1=(56,82,150), c2=(56,82,150)
v1=1, v2=0, c1=(58,82,150), c2=(58,82,150)
v1=1, v2=0, c1=(60,82,150), c2=(60,82,150)
v1=1, v2=0, c1=(62,82,150), c2=(62,82,150)
v1=1, v2=0, c1=(64,82,150), c2=(64,82,150)
v1=1, v2=0, c1=(66,82,150), c2=(66,82,150)
v1=1, v2=0, c1=(68,82,150), c2=(68,82,150)
v1=1, v2=0, c1=(70,82,150), c2=(70,82,150)
v1=1, v2=0, c1=(72,82,150), c2=(72,82,150)
v1=1, v2=0, c1=(74,82,150), c2=(74,82,150)
v1=1, v2=0, c1=(26,84,150), c2=(26,84,150)
v1=1, v2=0, c1=(28,84,150), c2=(28,84,150)
v1=1, v2=0, c1=(30,84,150), c2=(30,84,150)
v1=1, v2=0, c1=(32,84,150), c2=(32,84,150)
v1=1, v2=0, c1=(34,84,150), c2=(34,84,150)
v1=1, v2=0, c1=(36,84,150), c2=(36,84,150)
v1=1, v2=0, c1=(38,84,150), c2=(38,84,150)
v1=1, v2=0, c1=(40,84,150), c2=(40,84,150)
v1=1, v2=0, c1=(42,84,150), c2=(42,84,150)
v1=1, v2=0, c1=(44,84,150), c2=(44,84,150)
v1=1, v2=0, c1=(46,84,150), c2=(46,84,150)
v1=1, v2=0, c1=(48,84,150), c2=(48,84,150)
v1=1, v2=0, c1=(50,84,150), c2=(50,84,150)
v1=1, v2=0, c1=(52,84,150), c2=(52,84,150)
v1=1, v2=0, c1=(54,84,150), c2=(54,84,150)
v1=1, v2=0, c1=(56,84,150), c2=(56,84,150)
v1=1, v2=0, c1=(58,84,150), c2=(58,84,150)
v1=1, v2=0, c1=(60,84,150), c2=(60,84,150)
v1=1, v2=0, c1=(62,84,150), c2=(62,84,150)
v1=1, v2=0, c1=(64,84,150), c2=(64,84,150)
v1=1, v2=0, c1=(66,84,150), c2=(66,84,150)
v1=1, v2=0, c1=(68,84,150), c2=(68,84,150)
v1=1, v2=0, c1=(70,84,150), c2=(70,84,150)
v1=1, v2=0, c1=(72,84,150), c2=(72,84,150)
v1=1, v2=0, c1=(74,84,150), c2=(74,84,150)
v1=1, v2=0, c1=(24,86,150), c2=(24,86,150)
v1=1, v2=0, c1=(26,86,150), c2=(26,86,150)
v1=1, v2=0, c1=(28,86,150), c2=(28,86,150)
v1=1, v2=0, c1=(30,86,150), c2=(30,86,150)
v1=1, v2=0, c1=(32,86,150), c2=(32,86,150)
v1=1, v2=0, c1=(34,86,150), c2=(34,86,150)
v1=1, v2=0, c1=(36,86,150), c2=(36,86,150)
v1=1, v2=0, c1=(38,86,150), c2=(38,86,150)
v1=1, v2=0, c1=(40,86,150), c2=(40,86,150)
v1=1, v2=0, c1=(42,86,150), c2=(42,86,150)
v1=1, v2=0, c1=(44,86,150), c2=(44,86,150)
v1=1, v2=0, c1=(46,86,150), c2=(46,86,150)
v1=1, v2=0, c1=(48,86,150), c2=(48,86,150)
v1=1, v2=0, c1=(50,86,150), c2=(50,86,150)
v1=1, v2=0, c1=(52,86,150), c2=(52,86,150)
v1=1, v2=0, c1=(54,86,150), c2=(54,86,150)
v1=1, v2=0, c1=(56,86,150), c2=(56,86,150)
v1=1, v2=0, c1=(58,86,150), c2=(58,86,150)
v1=1, v2=0, c1=(60,86,150), c2=(60,86,150)
v1=1, v2=0, c1=(62,86,150), c2=(62,86,150)
v1=1, v2=0, c1=(64,86,150), c2=(64,86,150)
v1=1, v2=0, c1=(66,86,150), c2=(66,86,150)
v1=1, v2=0, c1=(68,86,150), c2=(68,86,150)
v1=1, v2=0, c1=(70,86,150), c2=(70,86,150)
v1=1, v2=0, c1=(72,86,150), c2=(72,86,150)
v1=1, v2=0, c1=(74,86,150), c2=(74,86,150)
v1=1, v2=0, c1=(76,86,150), c2=(76,86,150)
v1=1, v2=0, c1=(24,88,150), c2=(24,88,150)
v1=1, v2=0, c1=(26,88,150), c2=(26,88,150)
v1=1, v2=0, c1=(28,88,150), c2=(28,88,150)
v1=1, v2=0, c1=(30,88,150), c2=(30,88,150)
v1=1, v2=0, c1=(32,88,150), c2=(32,88,150)
v1=1, v2=0, c1=(34,88,150), c2=(34,88,150)
v1=1, v2=0, c1=(36,88,150), c2=(36,88,150)
v1=1, v2=0, c1=(38,88,150), c2=(38,88,150)
v1=1, v2=0, c1=(40,88,150), c2=(40,88,150)
v1=1, v2=0, c1=(42,88,150), c2=(42,88,150)
v1=1, v2=0, c1=(44,88,150), c2=(44,88,150)
v1=1, v2=0, c1=(46,88,150), c2=(46,88,150)
v1=1, v2=0, c1=(48,88,150), c2=(48,88,150)
v1=1, v2=0, c1=(50,88,150), c2=(50,88,150)
v1=1, v2=0, c1=(52,88,150), c2=(52,88,150)
v1=1, v2=0, c1=(54,88,150), c2=(54,88,150)
v1=1, v2=0, c1=(56,88,150), c2=(56,88,150)
v1=1, v2=0, c1=(58,88,150), c2=(58,88,150)
v1=1, v2=0, c1=(60,88,150), c2=(60,88,150)
v1=1, v2=0, c1=(62,88,150), c2=(62,88,150)
v1=1, v2=0, c1=(64,88,150), c2=(64,88,150)
v1=1, v2=0, c1=(66,88,150), c2=(66,88,150)
v1=1, v2=0, c1=(68,88,150), c2=(68,88,150)
v1=1, v2=0, c1=(70,88,150), c2=(70,88,150)
v1=1, v2=0, c1=(72,88,150), c2=(72,88,150)
v1=1, v2=0, c1=(74,88,150), c2=(74,88,150)
v1=1, v2=0, c1=(76,88,150), c2=(76,88,150)
v1=1, v2=0, c1=(22,90,150), c2=(22,90,150)
v1=1, v2=0, c1=(24,90,150), c2=(24,90,150)
v1=1, v2=0, c1=(26,90,150), c2=(26,90,150)
v1=1, v2=0, c1=(28,90,150), c2=(28,90,150)
v1=1, v2=0, c1=(30,90,150), c2=(30,90,150)
v1=1, v2=0, c1=(32,90,150), c2=(32,90,150)
v1=1, v2=0, c1=(34,90,150), c2=(34,90,150)
v1=1, v2=0, c1=(36,90,150), c2=(36,90,150)
v1=1, v2=0, c1=(38,90,150), c2=(38,90,150)
v1=1, v2=0, c1=(40,90,150), c2=(40,90,150)
v1=1, v2=0, c1=(42,90,150), c2=(42,90,150)
v1=1, v2=0, c1=(44,90,150), c2=(44,90,150)
v1=1, v2=0, c1=(46,90,150), c2=(46,90,150)
v1=1, v2=0, c1=(48,90,150), c2=(48,90,150)
v1=1, v2=0, c1=(50,90,150), c2=(50,90,150)
v1=1, v2=0, c1=(52,90,150), c2=(52,90,150)
v1=1, v2=0, c1=(54,90,150), c2=(54,90,150)
v1=1, v2=0, c1=(56,90,150), c2=(56,90,150)
v1=1, v2=0, c1=(58,90,150), c2=(58,90,150)
v1=1, v2=0, c1=(60,90,150), c2=(60,90,150)
v1=1, v2=0, c1=(62,90,150), c2=(62,90,150)
v1=1, v2=0, c1=(64,90,150), c2=(64,90,150)
v1=1, v2=0, c1=(66,90,150), c2=(66,90,150)
v1=1, v2=0, c1=(68,90,150), c2=(68,90,150)
v1=1, v2=0, c1=(70,90,150), c2=(70,90,150)
v1=1, v2=0, c1=(72,90,150), c2=(72,90,150)
v1=1, v2=0, c1=(74,90,150), c2=(74,90,150)
v1=1, v2=0, c1=(76,90,150), c2=(76,90,150)
v1=1, v2=0, c1=(78,90,150), c2=(78,90,150)
v1=1, v2=0, c1=(22,92,150), c2=(22,92,150)
v1=1, v2=0, c1=(24,92,150), c2=(24,92,150)
v1=1, v2=0, c1=(26,92,150), c2=(26,92,150)
v1=1, v2=0, c1=(28,92,150), c2=(28,92,150)
v1=1, v2=0, c1=(30,92,150), c2=(30,92,150)
v1=1, v2=0, c1=(32,92,150), c2=(32,92,150)
v1=1, v2=0, c1=(34,92,150), c2=(34,92,150)
v1=1, v2=0, c1=(36,92,150), c2=(36,92,150)
v1=1, v2=0, c1=(38,92,150), c2=(38,92,150)
v1=1, v2=0, c1=(40,92,150), c2=(40,92,150)
v1=1, v2=0, c1=(42,92,150), c2=(42,92,150)
v1=1, v2=0, c1=(44,92,150), c2=(44,92,150)
v1=1, v2=0, c1=(46,92,150), c2=(46,92,150)
v1=1, v2=0, c1=(48,92,150), c2=(48,92,150)
v1=1, v2=0, c1=(50,92,150), c2=(50,92,150)
v1=1, v2=0, c1=(52,92,150), c2=(52,92,150)
v1=1, v2=0, c1=(54,92,150), c2=(54,92,150)
v1=1, v2=0, c1=(56,92,150), c2=(56,92,150)
v1=1, v2=0, c1=(58,92,150), c2=(58,92,150)
v1=1, v2=0, c1=(60,92,150), c2=(60,92,150)
v1=1, v2=0, c1=(62,92,150), c2=(62,92,150)
v1=1, v2=0, c1=(64,92,150), c2=(64,92,150)
v1=1, v2=0, c1=(66,92,150), c2=(66,92,150)
v1=1, v2=0, c1=(68,92,150), c2=(68,92,150)
v1=1, v2=0, c1=(70,92,150), c2=(70,92,150)
v1=1, v2=0, c1=(72,92,150), c2=(72,92,150)
v1=1, v2=0, c1=(74,92,150), c2=(74,92,150)
v1=1, v2=0, c1=(76,92,150), c2=(76,92,150)
v1=1, v2=0, c1=(78,92,150), c2=(78,92,150)
v1=1, v2=0, c1=(22,94,150), c2=(22,94,150)
v1=1, v2=0, c1=(24,94,150), c2=(24,94,150)
v1=1, v2=0, c1=(26,94,150), c2=(26,94,150)
v1=1, v2=0, c1=(28,94,150), c2=(28,94,150)
v1=1, v2=0, c1=(30,94,150), c2=(30,94,150)
v1=1, v2=0, c1=(32,94,150), c2=(32,94,150)
v1=1, v2=0, c1=(34,94,150), c2=(34,94,150)
v1=1, v2=0, c1=(36,94,150), c2=(36,94,150)
v1=1, v2=0, c1=(38,94,150), c2=(38,94,150)
v1=1, v2=0, c1=(40,94,150), c2=(40,94,150)
v1=1, v2=0, c1=(42,94,150), c2=(42,94,150)
v1=1, v2=0, c1=(44,94,150), c2=(44,94,150)
v1=1, v2=0, c1=(46,94,150), c2=(46,94,150)
v1=1, v2=0, c1=(48,94,150), c2=(48,94,150)
v1=1, v2=0, c1=(50,94,150), c2=(50,94,150)
v1=1, v2=0, c1=(52,94,150), c2=(52,94,150)
v1=1, v2=0, c1=(54,94,150), c2=(54,94,150)
v1=1, v2=0, c1=(56,94,150), c2=(56,94,150)
v1=1, v2=0, c1=(58,94,150), c2=(58,94,150)
v1=1, v2=0, c1=(60,94,150), c2=(60,94,150)
v1=1, v2=0, c1=(62,94,150), c2=(62,94,150)
v1=1, v2=0, c1=(64,94,150), c2=(64,94,150)
v1=1, v2=0, c1=(66,94,150), c2=(66,94,150)
v1=1, v2=0, c1=(68,94,150), c2=(68,94,150)
v1=1, v2=0, c1=(70,94,150), c2=(70,94,150)
v1=1, v2=0, c1=(72,94,150), c2=(72,94,150)
v1=1, v2=0, c1=(74,94,150), c2=(74,94,150)
v1=1, v2=0, c1=(76,94,150), c2=(76,94,150)
v1=1, v2=0, c1=(78,94,150), c2=(78,94,150)
v1=1, v2=0, c1=(22,96,150), c2=(22,96,150)
v1=1, v2=0, c1=(24,96,150), c2=(24,96,150)
v1=1, v2=0, c1=(26,96,150), c2=(26,96,150)
v1=1, v2=0, c1=(28,96,150), c2=(28,96,150)
v1=1, v2=0, c1=(30,96,150), c2=(30,96,150)
v1=1, v2=0, c1=(32,96,150), c2=(32,96,150)
v1=1, v2=0, c1=(34,96,150), c2=(34,96,150)
v1=1, v2=0, c1=(36,96,150), c2=(36,96,150)
v1=1, v2=0, c1=(38,96,150), c2=(38,96,150)
v1=1, v2=0, c1=(40,96,150), c2=(40,96,150)
v1=1, v2=0, c1=(42,96,150), c2=(42,96,150)
v1=1, v2=0, c1=(44,96,150), c2=(44,96,150)
v1=1, v2=0, c1=(46,96,150), c2=(46,96,150)
v1=1, v2=0, c1=(48,96,150), c2=(48,96,150)
v1=1, v2=0, c1=(50,96,150), c2=(50,96,150)
v1=1, v2=0, c1=(52,96,150), c2=(52,96,150)
v1=1, v2=0, c1=(54,96,150), c2=(54,96,150)
v1=1, v2=0, c1=(56,96,150), c2=(56,96,150)
v1=1, v2=0, c1=(58,96,150), c2=(58,96,150)
v1=1, v2=0, c1=(60,96,150), c2=(60,96,150)
v1=1, v2=0, c1=(62,96,150), c2=(62,96,150)
v1=1, v2=0, c1=(64,96,150), c2=(64,96,150)
v1=1, v2=0, c1=(66,96,150), c2=(66,96,150)
v1=1, v2=0, c1=(68,96,150), c2=(68,96,150)
v1=1, v2=0, c1=(70,96,150), c2=(70,96,150)
v1=1, v2=0, c1=(72,96,150), c2=(72,96,150)
v1=1, v2=0, c1=(74,96,150), c2=(74,96,150)
v1=1, v2=0, c1=(76,96,150), c2=(76,96,150)
v1=1, v2=0, c1=(78,96,150), c2=(78,96,150)
v1=1, v2=0, c1=(22,98,150), c2=(22,98,150)
v1=1, v2=0, c1=(24,98,150), c2=(24,98,150)
v1=1, v2=0, c1=(26,98,150), c2=(26,98,150)
v1=1, v2=0, c1=(28,98,150), c2=(28,98,150)
v1=1, v2=0, c1=(30,98,150), c2=(30,98,150)
v1=1, v2=0, c1=(32,98,150), c2=(32,98,150)
v1=1, v2=0, c1=(34,98,150), c2=(34,98,150)
v1=1, v2=0, c1=(36,98,150), c2=(36,98,150)
v1=1, v2=0, c1=(38,98,150), c2=(38,98,150)
v1=1, v2=0, c1=(40,98,150), c2=(40,98,150)
v1=1, v2=0, c1=(42,98,150), c2=(42,98,150)
v1=1, v2=0, c1=(44,98,150), c2=(44,98,150)
v1=1, v2=0, c1=(46,98,150), c2=(46,98,150)
v1=1, v2=0, c1=(48,98,150), c2=(48,98,150)
v1=1, v2=0, c1=(50,98,150), c2=(50,98,150)
v1=1, v2=0, c1=(52,98,150), c2=(52,98,150)
v1=1, v2=0, c1=(54,98,150), c2=(54,98,150)
v1=1, v2=0, c1=(56,98,150), c2=(56,98,150)
v1=1, v2=0, c1=(58,98,150), c2=(58,98,150)
v1=1, v2=0, c1=(60,98,150), c2=(60,98,150)
v1=1, v2=0, c1=(62,98,150), c2=(62,98,150)
v1=1, v2=0, c1=(64,98,150), c2=(64,98,150)
v1=1, v2=0, c1=(66,98,150), c2=(66,98,150)
v1=1, v2=0, c1=(68,98,150), c2=(68,98,150)
v1=1, v2=0, c1=(70,98,150), c2=(70,98,150)
v1=1, v2=0, c1=(72,98,150), c2=(72,98,150)
v1=1, v2=0, c1=(74,98,150), c2=(74,98,150)
v1=1, v2=0, c1=(76,98,150), c2=(76,98,150)
v1=1, v2=0, c1=(78,98,150), c2=(78,98,150)
v1=1, v2=0, c1=(20,100,150), c2=(20,100,150)
v1=1, v2=0, c1=(22,100,150), c2=(22,100,150)
v1=1, v2=0, c1=(24,100,150), c2=(24,100,150)
v1=1, v2=0, c1=(26,100,150), c2=(26,100,150)
v1=1, v2=0, c1=(28,100,150), c2=(28,100,150)
v1=1, v2=0, c1=(30,100,150), c2=(30,100,150)
v1=1, v2=0, c1=(32,100,150), c2=(32,100,150)
v1=1, v2=0, c1=(34,100,150), c2=(34,100,150)
v1=1, v2=0, c1=(36,100,150), c2=(36,100,150)
v1=1, v2=0, c1=(38,100,150), c2=(38,100,150)
v1=1, v2=0, c1=(40,100,150), c2=(40,100,150)
v1=1, v2=0, c1=(42,100,150), c2=(42,100,150)
v1=1, v2=0, c1=(44,100,150), c2=(44,100,150)
v1=1, v2=0, c1=(46,100,150), c2=(46,100,150)
v1=1, v2=0, c1=(48,100,150), c2=(48,100,150)
v1=1, v2=0, c1=(50,100,150), c2=(50,100,150)
v1=1, v2=0, c1=(52,100,150), c2=(52,100,150)
v1=1, v2=0, c1=(54,100,150), c2=(54,100,150)
v1=1, v2=0, c1=(56,100,150), c2=(56,100,150)
v1=1, v2=0, c1=(58,100,150), c2=(58,100,150)
v1=1, v2=0, c1=(60,100,150), c2=(60,100,150)
v1=1, v2=0, c1=(62,100,150), c2=(62,100,150)
v1=1, v2=0, c1=(64,100,150), c2=(64,100,150)
v1=1, v2=0, c1=(66,100,150), c2=(66,100,150)
v1=1, v2=0, c1=(68,100,150), c2=(68,100,150)
v1=1, v2=0, c1=(70,100,150), c2=(70,100,150)
v1=1, v2=0, c1=(72,100,150), c2=(72,100,150)
v1=1, v2=0, c1=(74,100,150), c2=(74,100,150)
v1=1, v2=0, c1=(76,100,150), c2=(76,100,150)
v1=1, v2=0, c1=(78,100,150), c2=(78,100,150)
v1=1, v2=0, c1=(80,100,150), c2=(80,100,150)
v1=1, v2=0, c1=(22,102,150), c2=(22,102,150)
v1=1, v2=0, c1=(24,102,150), c2=(24,102,150)
v1=1, v2=0, c1=(26,102,150), c2=(26,102,150)
v1=1, v2=0, c1=(28,102,150), c2=(28,102,150)
v1=1, v2=0, c1=(30,102,150), c2=(30,102,150)
v1=1, v2=0, c1=(32,102,150), c2=(32,102,150)
v1=1, v2=0, c1=(34,102,150), c2=(34,102,150)
v1=1, v2=0, c1=(36,102,150), c2=(36,102,150)
v1=1, v2=0, c1=(38,102,150), c2=(38,102,150)
v1=1, v2=0, c1=(40,102,150), c2=(40,102,150)
v1=1, v2=0, c1=(42,102,150), c2=(42,102,150)
v1=1, v2=0, c1=(44,102,150), c2=(44,102,150)
v1=1, v2=0, c1=(46,102,150), c2=(46,102,150)
v1=1, v2=0, c1=(48,102,150), c2=(48,102,150)
v1=1, v2=0, c1=(50,102,150), c2=(50,102,150)
v1=1, v2=0, c1=(52,102,150), c2=(52,102,150)
v1=1, v2=0, c1=(54,102,150), c2=(54,102,150)
v1=1, v2=0, c1=(56,102,150), c2=(56,102,150)
v1=1, v2=0, c1=(58,102,150), c2=(58,102,150)
v1=1, v2=0, c1=(60,102,150), c2=(60,102,150)
v1=1, v2=0, c1=(62,102,150), c2=(62,102,150)
v1=1, v2=0, c1=(64,102,150), c2=(64,102,150)
v1=1, v2=0, c1=(66,102,150), c2=(66,102,150)
v1=1, v2=0, c1=(68,102,150), c2=(68,102,150)
v1=1, v2=0, c1=(70,102,150), c2=(70,102,150)
v1=1, v2=0, c1=(72,102,150), c2=(72,102,150)
v1=1, v2=0, c1=(74,102,150), c2=(74,102,150)
v1=1, v2=0, c1=(76,102,150), c2=(76,102,150)
v1=1, v2=0, c1=(78,102,150), c2=(78,102,150)
v1=1, v2=0, c1=(22,104,150), c2=(22,104,150)
v1=1, v2=0, c1=(24,104,150), c2=(24,104,150)
v1=1, v2=0, c1=(26,104,150), c2=(26,104,150)
v1=1, v2=0, c1=(28,104,150), c2=(28,104,150)
v1=1, v2=0, c1=(30,104,150), c2=(30,104,150)
v1=1, v2=0, c1=(32,104,150), c2=(32,104,150)
v1=1, v2=0, c1=(34,104,150), c2=(34,104,150)
v1=1, v2=0, c1=(36,104,150), c2=(36,104,150)
v1=1, v2=0, c1=(38,104,150), c2=(38,104,150)
v1=1, v2=0, c1=(40,104,150), c2=(40,104,150)
v1=1, v2=0, c1=(42,104,150), c2=(42,104,150)
v1=1, v2=0, c1=(44,104,150), c2=(44,104,150)
v1=1, v2=0, c1=(46,104,150), c2=(46,104,150)
v1=1, v2=0, c1=(48,104,150), c2=(48,104,150)
v1=1, v2=0, c1=(50,104,150), c2=(50,104,150)
v1=1, v2=0, c1=(52,104,150), c2=(52,104,150)
v1=1, v2=0, c1=(54,104,150), c2=(54,104,150)
v1=1, v2=0, c1=(56,104,150), c2=(56,104,150)
v1=1, v2=0, c1=(58,104,150), c2=(58,104,150)
v1=1, v2=0, c1=(60,104,150), c2=(60,104,150)
v1=1, v2=0, c1=(62,104,150), c2=(62,104,150)
v1=1, v2=0, c1=(64,104,150), c2=(64,104,150)
v1=1, v2=0, c1=(66,104,150), c2=(66,104,150)
v1=1, v2=0, c1=(68,104,150), c2=(68,104,150)
v1=1, v2=0, c1=(70,104,150), c2=(70,104,150)
v1=1, v2=0, c1=(72,104,150), c2=(72,104,150)
v1=1, v2=0, c1=(74,104,150), c2=(74,104,150)
v1=1, v2=0, c1=(76,104,150), c2=(76,104,150)
v1=1, v2=0, c1=(78,104,150), c2=(78,104,150)
v1=1, v2=0, c1=(22,106,150), c2=(22,106,150)
v1=1, v2=0, c1=(24,106,150), c2=(24,106,150)
v1=1, v2=0, c1=(26,106,150), c2=(26,106,150)
v1=1, v2=0, c1=(28,106,150), c2=(28,106,150)
v1=1, v2=0, c1=(30,106,150), c2=(30,106,150)
v1=1, v2=0, c1=(32,106,150), c2=(32,106,150)
v1=1, v2=0, c1=(34,106,150), c2=(34,106,150)
v1=1, v2=0, c1=(36,106,150), c2=(36,106,150)
v1=1, v2=0, c1=(38,106,150), c2=(38,106,150)
v1=1, v2=0, c1=(40,106,150), c2=(40,106,150)
v1=1, v2=0, c1=(42,106,150), c2=(42,106,150)
v1=1, v2=0, c1=(44,106,150), c2=(44,106,150)
v1=1, v2=0, c1=(46,106,150), c2=(46,106,150)
v1=1, v2=0, c1=(48,106,150), c2=(48,106,150)
v1=1, v2=0, c1=(50,106,150), c2=(50,106,150)
v1=1, v2=0, c1=(52,106,150), c2=(52,106,150)
v1=1, v2=0, c1=(54,106,150), c2=(54,106,150)
v1=1, v2=0, c1=(56,106,150), c2=(56,106,150)
v1=1, v2=0, c1=(58,106,150), c2=(58,106,150)
v1=1, v2=0, c1=(60,106,150), c2=(60,106,150)
v1=1, v2=0, c1=(62,106,150), c2=(62,106,150)
v1=1, v2=0, c1=(64,106,150), c2=(64,106,150)
v1=1, v2=0, c1=(66,106,150), c2=(66,106,150)
v1=1, v2=0, c1=(68,106,150), c2=(68,106,150)
v1=1, v2=0, c1=(70,106,150), c2=(70,106,150)
v1=1, v2=0, c1=(72,106,150), c2=(72,106,150)
v1=1, v2=0, c1=(74,106,150), c2=(74,106,150)
v1=1, v2=0, c1=(76,106,150), c2=(76,106,150)
v1=1, v2=0, c1=(78,106,150), c2=(78,106,150)
v1=1, v2=0, c1=(22,108,150), c2=(22,108,150)
v1=1, v2=0, c1=(24,108,150), c2=(24,108,150)
v1=1, v2=0, c1=(26,108,150), c2=(26,108,150)
v1=1, v2=0, c1=(28,108,150), c2=(28,108,150)
v1=1, v2=0, c1=(30,108,150), c2=(30,108,150)
v1=1, v2=0, c1=(32,108,150), c2=(32,108,150)
v1=1, v2=0, c1=(34,108,150), c2=(34,108,150)
v1=1, v2=0, c1=(36,108,150), c2=(36,108,150)
v1=1, v2=0, c1=(38,108,150), c2=(38,108,150)
v1=1, v2=0, c1=(40,108,150), c2=(40,108,150)
v1=1, v2=0, c1=(42,108,150), c2=(42,108,150)
v1=1, v2=0, c1=(44,108,150), c2=(44,108,150)
v1=1, v2=0, c1=(46,108,150), c2=(46,108,150)
v1=1, v2=0, c1=(48,108,150), c2=(48,108,150)
v1=1, v2=0, c1=(50,108,150), c2=(50,108,150)
v1=1, v2=0, c1=(52,108,150), c2=(52,108,150)
v1=1, v2=0, c1=(54,108,150), c2=(54,108,150)
v1=1, v2=0, c1=(56,108,150), c2=(56,108,150)
v1=1, v2=0, c1=(58,108,150), c2=(58,108,150)
v1=1, v2=0, c1=(60,108,150), c2=(60,108,150)
v1=1, v2=0, c1=(62,108,150), c2=(62,108,150)
v1=1, v2=0, c1=(64,108,150), c2=(64,108,150)
v1=1, v2=0, c1=(66,108,150), c2=(66,108,150)
v1=1, v2=0, c1=(68,108,150), c2=(68,108,150)
v1=1, v2=0, c1=(70,108,150), c2=(70,108,150)
v1=1, v2=0, c1=(72,108,150), c2=(72,108,150)
v1=1, v2=0, c1=(74,108,150), c2=(74,108,150)
v1=1, v2=0, c1=(76,108,150), c2=(76,108,150)
v1=1, v2=0, c1=(78,108,150), c2=(78,108,150)
v1=1, v2=0, c1=(22,110,150), c2=(22,110,150)
v1=1, v2=0, c1=(24,110,150), c2=(24,110,150)
v1=1, v2=0, c1=(26,110,150), c2=(26,110,150)
v1=1, v2=0, c1=(28,110,150), c2=(28,110,150)
v1=1, v2=0, c1=(30,110,150), c2=(30,110,150)
v1=1, v2=0, c1=(32,110,150), c2=(32,110,150)
v1=1, v2=0, c1=(34,110,150), c2=(34,110,150)
v1=1, v2=0, c1=(36,110,150), c2=(36,110,150)
v1=1, v2=0, c1=(38,110,150), c2=(38,110,150)
v1=1, v2=0, c1=(40,110,150), c2=(40,110,150)
v1=1, v2=0, c1=(42,110,150), c2=(42,110,150)
v1=1, v2=0, c1=(44,110,150), c2=(44,110,150)
v1=1, v2=0, c1=(46,110,150), c2=(46,110,150)
v1=1, v2=0, c1=(48,110,150), c2=(48,110,150)
v1=1, v2=0, c1=(50,110,150), c2=(50,110,150)
v1=1, v2=0, c1=(52,110,150), c2=(52,110,150)
v1=1, v2=0, c1=(54,110,150), c2=(54,110,150)
v1=1, v2=0, c1=(56,110,150), c2=(56,110,150)
v1=1, v2=0, c1=(58,110,150), c2=(58,110,150)
v1=1, v2=0, c1=(60,110,150), c2=(60,110,150)
v1=1, v2=0, c1=(62,110,150), c2=(62,110,150)
v1=1, v2=0, c1=(64,110,150), c2=(64,110,150)
v1=1, v2=0, c1=(66,110,150), c2=(66,110,150)
v1=1, v2=0, c1=(68,110,150), c2=(68,110,150)
v1=1, v2=0, c1=(70,110,150), c2=(70,110,150)
v1=1, v2=0, c1=(72,110,150), c2=(72,110,150)
v1=1, v2=0, c1=(74,110,150), c2=(74,110,150)
v1=1, v2=0, c1=(76,110,150), c2=(76,110,150)
v1=1, v2=0, c1=(78,110,150), c2=(78,110,150)
v1=1, v2=0, c1=(24,112,150), c2=(24,112,150)
v1=1, v2=0, c1=(26,112,150), c2=(26,112,150)
v1=1, v2=0, c1=(28,112,150), c2=(28,112,150)
v1=1, v2=0, c1=(30,112,150), c2=(30,112,150)
v1=1, v2=0, c1=(32,112,150), c2=(32,112,150)
v1=1, v2=0, c1=(34,112,150), c2=(34,112,150)
v1=1, v2=0, c1=(36,112,150), c2=(36,112,150)
v1=1, v2=0, c1=(38,112,150), c2=(38,112,150)
v1=1, v2=0, c1=(40,112,150), c2=(40,112,150)
v1=1, v2=0, c1=(42,112,150), c2=(42,112,150)
v1=1, v2=0, c1=(44,112,150), c2=(44,112,150)
v1=1, v2=0, c1=(46,112,150), c2=(46,112,150)
v1=1, v2=0, c1=(48,112,150), c2=(48,112,150)
v1=1, v2=0, c1=(50,112,150), c2=(50,112,150)
v1=1, v2=0, c1=(52,112,150), c2=(52,112,150)
v1=1, v2=0, c1=(54,112,150), c2=(54,112,150)
v1=1, v2=0, c1=(56,112,150), c2=(56,112,150)
v1=1, v2=0, c1=(58,112,150), c2=(58,112,150)
v1=1, v2=0, c1=(60,112,150), c2=(60,112,150)
v1=1, v2=0, c1=(62,112,150), c2=(62,112,150)
v1=1, v2=0, c1=(64,112,150), c2=(64,112,150)
v1=1, v2=0, c1=(66,112,150), c2=(66,112,150)
v1=1, v2=0, c1=(68,112,150), c2=(68,112,150)
v1=1, v2=0, c1=(70,112,150), c2=(70,112,150)
v1=1, v2=0, c1=(72,112,150), c2=(72,112,150)
v1=1, v2=0, c1=(74,112,150), c2=(74,112,150)
v1=1, v2=0, c1=(76,112,150), c2=(76,112,150)
v1=1, v2=0, c1=(24,114,150), c2=(24,114,150)
v1=1, v2=0, c1=(26,114,150), c2=(26,114,150)
v1=1, v2=0, c1=(28,114,150), c2=(28,114,150)
v1=1, v2=0, c1=(30,114,150), c2=(30,114,150)
v1=1, v2=0, c1=(32,114,150), c2=(32,114,150)
v1=1, v2=0, c1=(34,114,150), c2=(34,114,150)
v1=1, v2=0, c1=(36,114,150), c2=(36,114,150)
v1=1, v2=0, c1=(38,114,150), c2=(38,114,150)
v1=1, v2=0, c1=(40,114,150), c2=(40,114,150)
v1=1, v2=0, c1=(42,114,150), c2=(42,114,150)
v1=1, v2=0, c1=(44,114,150), c2=(44,114,150)
v1=1, v2=0, c1=(46,114,150), c2=(46,114,150)
v1=1, v2=0, c1=(48,114,150), c2=(48,114,150)
v1=1, v2=0, c1=(50,114,150), c2=(50,114,150)
v1=1, v2=0, c1=(52,114,150), c2=(52,114,150)
v1=1, v2=0, c1=(54,114,150), c2=(54,114,150)
v1=1, v2=0, c1=(56,114,150), c2=(56,114,150)
v1=1, v2=0, c1=(58,114,150), c2=(58,114,150)
v1=1, v2=0, c1=(60,114,150), c2=(60,114,150)
v1=1, v2=0, c1=(62,114,150), c2=(62,114,150)
v1=1, v2=0, c1=(64,114,150), c2=(64,114,150)
v1=1, v2=0, c1=(66,114,150), c2=(66,114,150)
v1=1, v2=0, c1=(68,114,150), c2=(68,114,150)
v1=1, v2=0, c1=(70,114,150), c2=(70,114,150)
v1=1, v2=0, c1=(72,114,150), c2=(72,114,150)
v1=1, v2=0, c1=(74,114,150), c2=(74,114,150)
v1=1, v2=0, c1=(76,114,150), c2=(76,114,150)
v1=1, v2=0, c1=(26,116,150), c2=(26,116,150)
v1=1, v2=0, c1=(28,116,150), c2=(28,116,150)
v1=1, v2=0, c1=(30,116,150), c2=(30,116,150)
v1=1, v2=0, c1=(32,116,150), c2=(32,116,150)
v1=1, v2=0, c1=(34,116,150), c2=(34,116,150)
v1=1, v2=0, c1=(36,116,150), c2=(36,116,150)
v1=1, v2=0, c1=(38,116,150), c2=(38,116,150)
v1=1, v2=0, c1=(40,116,150), c2=(40,116,150)
v1=1, v2=0, c1=(42,116,150), c2=(42,116,150)
v1=1, v2=0, c1=(44,116,150), c2=(44,116,150)
v1=1, v2=0, c1=(46,116,150), c2=(46,116,150)
v1=1, v2=0, c1=(48,116,150), c2=(48,116,150)
v1=1, v2=0, c1=(50,116,150), c2=(50,116,150)
v1=1, v2=0, c1=(52,116,150), c2=(52,116,150)
v1=1, v2=0, c1=(54,116,150), c2=(54,116,150)
v1=1, v2=0, c1=(56,116,150), c2=(56,116,150)
v1=1, v2=0, c1=(58,116,150), c2=(58,116,150)
v1=1, v2=0, c1=(60,116,150), c2=(60,116,150)
v1=1, v2=0, c1=(62,116,150), c2=(62,116,150)
v1=1, v2=0, c1=(64,116,150), c2=(64,116,150)
v1=1, v2=0, c1=(66,116,150), c2=(66,116,150)
v1=1, v2=0, c1=(68,116,150), c2=(68,116,150)
v1=1, v2=0, c1=(70,116,150), c2=(70,116,150)
v1=1, v2=0, c1=(72,116,150), c2=(72,116,150)
v1=1, v2=0, c1=(74,116,150), c2=(74,116,150)
v1=1, v2=0, c1=(26,118,150), c2=(26,118,150)
v1=1, v2=0, c1=(28,118,150), c2=(28,118,150)
v1=1, v2=0, c1=(30,118,150), c2=(30,118,150)
v1=1, v2=0, c1=(32,118,150), c2=(32,118,150)
v1=1, v2=0, c1=(34,118,150), c2=(34,118,150)
v1=1, v2=0, c1=(36,118,150), c2=(36,118,150)
v1=1, v2=0, c1=(38,118,150), c2=(38,118,150)
v1=1, v2=0, c1=(40,118,150), c2=(40,118,150)
v1=1, v2=0, c1=(42,118,150), c2=(42,118,150)
v1=1, v2=0, c1=(44,118,150), c2=(44,118,150)
v1=1, v2=0, c1=(46,118,150), c2=(46,118,150)
v1=1, v2=0, c1=(48,118,150), c2=(48,118,150)
v1=1, v2=0, c1=(50,118,150), c2=(50,118,150)
v1=1, v2=0, c1=(52,118,150), c2=(52,118,150)
v1=1, v2=0, c1=(54,118,150), c2=(54,118,150)
v1=1, v2=0, c1=(56,118,150), c2=(56,118,150)
v1=1, v2=0, c1=(58,118,150), c2=(58,118,150)
v1=1, v2=0, c1=(60,118,150), c2=(60,118,150)
v1=1, v2=0, c1=(62,118,150), c2=(62,118,150)
v1=1, v2=0, c1=(64,118,150), c2=(64,118,150)
v1=1, v2=0, c1=(66,118,150), c2=(66,118,150)
v1=1, v2=0, c1=(68,118,150), c2=(68,118,150)
v1=1, v2=0, c1=(70,118,150), c2=(70,118,150)
v1=1, v2=0, c1=(72,118,150), c2=(72,118,150)
v1=1, v2=0, c1=(74,118,150), c2=(74,118,150)
v1=1, v2=0, c1=(28,120,150), c2=(28,120,150)
v1=1, v2=0, c1=(30,120,150), c2=(30,120,150)
v1=1, v2=0, c1=(32,120,150), c2=(32,120,150)
v1=1, v2=0, c1=(34,120,150), c2=(34,120,150)
v1=1, v2=0, c1=(36,120,150), c2=(36,120,150)
v1=1, v2=0, c1=(38,120,150), c2=(38,120,150)
v1=1, v2=0, c1=(40,120,150), c2=(40,120,150)
v1=1, v2=0, c1=(42,120,150), c2=(42,120,150)
v1=1, v2=0, c1=(44,120,150), c2=(44,120,150)
v1=1, v2=0, c1=(46,120,150), c2=(46,120,150)
v1=1, v2=0, c1=(48,120,150), c2=(48,120,150)
v1=1, v2=0, c1=(50,120,150), c2=(50,120,150)
v1=1, v2=0, c1=(52,120,150), c2=(52,120,150)
v1=1, v2=0, c1=(54,120,150), c2=(54,120,150)
v1=1, v2=0, c1=(56,120,150), c2=(56,120,150)
v1=1, v2=0, c1=(58,120,150), c2=(58,120,150)
v1=1, v2=0, c1=(60,120,150), c2=(60,120,150)
v1=1, v2=0, c1=(62,120,150), c2=(62,120,150)
v1=1, v2=0, c1=(64,120,150), c2=(64,120,150)
v1=1, v2=0, c1=(66,120,150), c2=(66,120,150)
v1=1, v2=0, c1=(68,120,150), c2=(68,120,150)
v1=1, v2=0, c1=(70,120,150), c2=(70,120,150)
v1=1, v2=0, c1=(72,120,150), c2=(72,120,150)
v1=1, v2=0, c1=(30,122,150), c2=(30,122,150)
v1=1, v2=0, c1=(32,122,150), c2=(32,122,150)
v1=1, v2=0, c1=(34,122,150), c2=(34,122,150)
v1=1, v2=0, c1=(36,122,150), c2=(36,122,150)
v1=1, v2=0, c1=(38,122,150), c2=(38,122,150)
v1=1, v2=0, c1=(40,122,150), c2=(40,122,150)
v1=1, v2=0, c1=(42,122,150), c2=(42,122,150)
v1=1, v2=0, c1=(44,122,150), c2=(44,122,150)
v1=1, v2=0, c1=(46,122,150), c2=(46,122,150)
v1=1, v2=0, c1=(48,122,150), c2=(48,122,150)
v1=1, v2=0, c1=(50,122,150), c2=(50,122,150)
v1=1, v2=0, c1=(52,122,150), c2=(52,122,150)
v1=1, v2=0, c1=(54,122,150), c2=(54,122,150)
v1=1, v2=0, c1=(56,122,150), c2=(56,122,150)
v1=1, v2=0, c1=(58,122,150), c2=(58,122,150)
v1=1, v2=0, c1=(60,122,150), c2=(60,122,150)
v1=1, v2=0, c1=(62,122,150), c2=(62,122,150)
v1=1, v2=0, c1=(64,122,150), c2=(64,122,150)
v1=1, v2=0, c1=(66,122,150), c2=(66,122,150)
v1=1, v2=0, c1=(68,122,150), c2=(68,122,150)
v1=1, v2=0, c1=(70,122,150), c2=(70,122,150)
v1=1, v2=0, c1=(32,124,150), c2=(32,124,150)
v1=1, v2=0, c1=(34,124,150), c2=(34,124,150)
v1=1, v2=0, c1=(36,124,150), c2=(36,124,150)
v1=1, v2=0, c1=(38,124,150), c2=(38,124,150)
v1=1, v2=0, c1=(40,124,150), c2=(40,124,150)
v1=1, v2=0, c1=(42,124,150), c2=(42,124,150)
v1=1, v2=0, c1=(44,124,150), c2=(44,124,150)
v1=1, v2=0, c1=(46,124,150), c2=(46,124,150)
v1=1, v2=0, c1=(48,124,150), c2=(48,124,150)
v1=1, v2=0, c1=(50,124,150), c2=(50,124,150)
v1=1, v2=0, c1=(52,124,150), c2=(52,124,150)
v1=1, v2=0, c1=(54,124,150), c2=(54,124,150)
v1=1, v2=0, c1=(56,124,150), c2=(56,124,150)
v1=1, v2=0, c1=(58,124,150), c2=(58,124,150)
v1=1, v2=0, c1=(60,124,150), c2=(60,124,150)
v1=1, v2=0, c1=(62,124,150), c2=(62,124,150)
v1=1, v2=0, c1=(64,124,150), c2=(64,124,150)
v1=1, v2=0, c1=(66,124,150), c2=(66,124,150)
v1=1, v2=0, c1=(68,124,150), c2=(68,124,150)
v1=1, v2=0, c1=(36,126,150), c2=(36,126,150)
v1=1, v2=0, c1=(38,126,150), c2=(38,126,150)
v1=1, v2=0, c1=(40,126,150), c2=(40,126,150)
v1=1, v2=0, c1=(42,126,150), c2=(42,126,150)
v1=1, v2=0, c1=(44,126,150), c2=(44,126,150)
v1=1, v2=0, c1=(46,126,150), c2=(46,126,150)
v1=1, v2=0, c1=(48,126,150), c2=(48,126,150)
v1=1, v2=0, c1=(50,126,150), c2=(50,126,150)
v1=1, v2=0, c1=(52,126,150), c2=(52,126,150)
v1=1, v2=0, c1=(54,126,150), c2=(54,126,150)
v1=1, v2=0, c1=(56,126,150), c2=(56,126,150)
v1=1, v2=0, c1=(58,126,150), c2=(58,126,150)
v1=1, v2=0, c1=(60,126,150), c2=(60,126,150)
v1=1, v2=0, c1=(62,126,150), c2=(62,126,150)
v1=1, v2=0, c1=(64,126,150), c2=(64,126,150)
v1=1, v2=0, c1=(40,128,150), c2=(40,128,150)
v1=1, v2=0, c1=(42,128,150), c2=(42,128,150)
v1=1, v2=0, c1=(44,128,150), c2=(44,128,150)
v1=1, v2=0, c1=(46,128,150), c2=(46,128,150)
v1=1, v2=0, c1=(48,128,150), c2=(48,128,150)
v1=1, v2=0, c1=(50,128,150), c2=(50,128,150)
v1=1, v2=0, c1=(52,128,150), c2=(52,128,150)
v1=1, v2=0, c1=(54,128,150), c2=(54,128,150)
v1=1, v2=0, c1=(56,128,150), c2=(56,128,150)
v1=1, v2=0, c1=(58,128,150), c2=(58,128,150)
v1=1, v2=0, c1=(60,128,150), c2=(60,128,150)
v1=1, v2=0, c1=(50,130,150), c2=(50,130,150)
count=710, sphere size=709
Equal? true
709

Watershed Labeling deprecated

I was gonna use the Watershed algorithm, but found that one of its inputs, namely Labeling is deprecated. I was wondering what I am supposed to do? Shall I try to use it like it is or wait for a newer version?

Unify multithreading handling

Many of the implementations in imglib2-algorithm have an int numThreads parameter for controlling how many threads to spawn. Others take an ExecutorService. Accepting an ExecutorService is more flexible and would mesh better with SciJava Common’s ThreadService and therefore ImageJ Ops. See also imagej/imagej-ops#599. On the other hand, an ExecutorService alone is not enough to identify the intended number of threads to use when partitioning the task.

Note that currently, we often infer numTasks from Runtime.availableProcessors() by default, which is not a good practice in general because multiple tasks may be ongoing simultaneously, which can result in more threads than processors.

@tpietzsch points out:

  • In ForkJoinPool there is a "parallelism level" that corresponds to this roughly. We should consider using ForkJoinPool throughout.
  • ForkJoinPool extends ExecutorService, so it would at least be backwards compatible in some ways.
  • ForkJoinPool.getParallelism() could replace / augment numTasks.
  • ForkJoinPool supports work-stealing which would be important if submitted task spawn new subtasks for whose completion they wait. This allows handing down pool through algorithms that parallelise in chunks and for each chunk call another algorithm that parallelizes internally. (With handing down ExecutorService that wouldn't work.)
  • Also there is the common ForkJoinPool.commonPool() that is used by streams etc. We could fall back to this there is no user-provided pool.

Based on a chat in gitter.

Don't print stack trace during InterruptedException.

I have a situation where I expect the user may modify the image I am running ConnectedComponents on, in which case I interrupt the existing attempt and retrigger with the new image. It works fine, but always prints the stack trace from the interrupted exception. I'm not sure how best to handle here, but given this is a blocking and potentially slow task, it may be best to just let the method throws InterruptedException? Or is there a better way to achieve this?

Get span of RectangleShape

Hi @tpietzsch !

I am currently working on some neighborhood operations code, especially for imagej-ops. I need to access the span of an existing RectangleShape, but neither Shape nor RectangleShape have a simple way to access it.

I guess you can probably get the span over the bounding box of the neighborhood, but that does not seem very nice.

Ideas:
Common for all shapes is a bounding box. What would you thing of a method to retrieve this bounding box/interval for example?
Another idea could be to have every Shape subclass provide getters for their parameters. Not all shapes have a span, obviously.

If you tell me, how you would want a solution to this to look, I could implement it.

Greeting,
Squareys (Jonathan)

EllipseTest is slow

I just noticed that the EllipseTest is very slow: In this example it took more than 11 seconds ( I added context for comparison).

Running net.imglib2.algorithm.region.EllipseTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.481 sec - in net.imglib2.algorithm.region.EllipseTest
Running net.imglib2.algorithm.hessian.HessianMatrixTest
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.057 sec - in net.imglib2.algorithm.hessian.HessianMatrixTest
Running net.imglib2.algorithm.hessian.HessianMatrixEigenValuesTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 sec - in net.imglib2.algorithm.hessian.HessianMatrixEigenValuesTest
Running net.imglib2.algorithm.localextrema.SubpixelLocalizationTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.051 sec - in net.imglib2.algorithm.localextrema.SubpixelLocalizationTest
Running net.imglib2.algorithm.region.EllipseTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.481 sec - in net.imglib2.algorithm.region.EllipseTest
Running net.imglib2.algorithm.region.CircleTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.228 sec - in net.imglib2.algorithm.region.CircleTest
Running tests.labeling.ConnectedComponentsTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.119 sec - in tests.labeling.ConnectedComponentsTest
Running tests.labeling.WatershedTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.017 sec - in tests.labeling.WatershedTest
Running tests.labeling.AllConnectedComponentsTest
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.039 sec - in tests.labeling.AllConnectedComponentsTest

This inreases the build-time of imglib2-algorithm unnecessarily.
testIterateOnce is run for 55 * 55 = 3025 times for all combinations for two radii 1 <= rx, ry <= 55 and creates a new String object in every iteration of the innermost loop. I also think the benefits of this methods are very limited as it essentially tests cursor iteration by cursor iteration.

testDistanceToCenter creates String objects in the innermost loop as well.

I suggest we remove testIterateOnce and avoid the creation of objects in the inner loop of testDistanceToCenter:

Running net.imglib2.algorithm.region.EllipseTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.036 sec - in net.imglib2.algorithm.region.EllipseTest

Please comment.

gradientCentralDifference sets wrong boundaries for random accesses

I encountered ArrayIndexOufOfBoundsException in calls to PartialDerivative.gradientCentralDifference. I was able to make minimum working example that reproduced that behavior:

import net.imglib2.algorithm.gradient.PartialDerivative;
import net.imglib2.cache.img.CellLoader;
import net.imglib2.cache.img.DiskCachedCellImg;
import net.imglib2.cache.img.DiskCachedCellImgFactory;
import net.imglib2.cache.img.DiskCachedCellImgOptions;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.Views;

public class PartialDerivativeBug
{

	public static void main( final String[] args )
	{

		final long[] dim = new long[] { 200, 300, 400 };

		final DiskCachedCellImgOptions opts = DiskCachedCellImgOptions.options().cellDimensions( 64, 64, 64 ).dirtyAccesses( false ).maxCacheSize( 100 );

		final DiskCachedCellImgFactory< FloatType > factory = new DiskCachedCellImgFactory<>( opts );

		final CellLoader< FloatType > loader1 = img -> {

		};

		final DiskCachedCellImg< FloatType, ? > img1 = factory.create( dim, new FloatType(), loader1 );

		final CellLoader< FloatType > loader2 = img -> {
			PartialDerivative.gradientCentralDifference( Views.extendBorder( img1 ), img, 2 );
		};

		final DiskCachedCellImg< FloatType, ? > gradient = factory.create( dim, new FloatType(), loader2 );

		for ( final FloatType g : gradient )
			g.get();


	}

}

This code throws the following Exception:

Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: -4096
	at net.imglib2.cache.util.CacheAsUncheckedCacheAdapter.get(CacheAsUncheckedCacheAdapter.java:32)
	at net.imglib2.img.cell.LazyCellImg$LazyCells.get(LazyCellImg.java:78)
	at net.imglib2.img.list.AbstractLongListImg$LongListCursor.get(AbstractLongListImg.java:98)
	at net.imglib2.img.cell.CellCursor.getCell(CellCursor.java:94)
	at net.imglib2.img.cell.CellCursor.moveToNextCell(CellCursor.java:182)
	at net.imglib2.img.cell.CellCursor.reset(CellCursor.java:152)
	at net.imglib2.img.cell.CellCursor.<init>(CellCursor.java:88)
	at net.imglib2.img.cell.AbstractCellImg.cursor(AbstractCellImg.java:92)
	at net.imglib2.img.cell.AbstractCellImg.cursor(AbstractCellImg.java:51)
	at net.imglib2.img.AbstractImg.iterator(AbstractImg.java:75)
	at de.hanslovsky.zspacing.spark.experiments.visualization.PartialDerivativeBug.main(PartialDerivativeBug.java:35)
Caused by: java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: -4096
	at net.imglib2.cache.ref.SoftRefLoaderRemoverCache.get(SoftRefLoaderRemoverCache.java:168)
	at net.imglib2.cache.util.LoaderRemoverCacheAsLoaderCacheAdapter.get(LoaderRemoverCacheAsLoaderCacheAdapter.java:37)
	at net.imglib2.cache.util.LoaderCacheAsCacheAdapter.get(LoaderCacheAsCacheAdapter.java:30)
	at net.imglib2.cache.util.CacheAsUncheckedCacheAdapter.get(CacheAsUncheckedCacheAdapter.java:28)
	... 10 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: -4096
	at net.imglib2.img.basictypeaccess.array.AbstractFloatArray.getValue(AbstractFloatArray.java:61)
	at net.imglib2.type.numeric.real.FloatType.get(FloatType.java:115)
	at net.imglib2.type.numeric.real.FloatType.sub(FloatType.java:198)
	at net.imglib2.type.numeric.real.FloatType.sub(FloatType.java:50)
	at net.imglib2.algorithm.gradient.PartialDerivative.gradientCentralDifference(PartialDerivative.java:198)
	at de.hanslovsky.zspacing.spark.experiments.visualization.PartialDerivativeBug.lambda$1(PartialDerivativeBug.java:30)
	at net.imglib2.cache.img.LoadedCellCacheLoader.get(LoadedCellCacheLoader.java:82)
	at net.imglib2.cache.img.LoadedCellCacheLoader.get(LoadedCellCacheLoader.java:44)
	at net.imglib2.cache.img.DiskCellCache.get(DiskCellCache.java:104)
	at net.imglib2.cache.img.DiskCellCache.get(DiskCellCache.java:43)
	at net.imglib2.cache.IoSync.get(IoSync.java:174)
	at net.imglib2.cache.ref.SoftRefLoaderRemoverCache.get(SoftRefLoaderRemoverCache.java:158)
	... 13 more

gradientCentralDifference tries to get the most efficient RandomAccesses by specifying the required intervals for the forward and backward terms of the finite difference sum:
https://github.com/imglib/imglib2-algorithm/blob/master/src/main/java/net/imglib2/algorithm/gradient/PartialDerivative.java#L183-L184
As far as I can tell, the specifications for the required intervals are wrong, though: back and front should be translated by -1 and 1, respectively. Currently, it is the other way round. As some implementations of RandomAccessible delegate randomAccess( Interval ) to randomAccess(), this issue does not happen a lot, in practice.

I will make a fix for this.

Remove imagej-common dependency

We should get rid of the imagej-common dependency. It is only needed in DoG to get image calibration from net.imagej.space.LinearSpace. This should not happen here. Just move it to ops.

RectangleNeighborhoodCursor.jumpFwd(1) results in incorrect position.

Hi @tpietzsch !

I believe I found a bug in RectangleNeighborhoodCursor:

On a fresh shape.neighborhoodsSafe(img).localizingCursor() fwd() results in position [0, 0, 0], but
jumpFwd(1) results in position [2, 0, 0].

I wrote a simple test for this: here

When letting the cursor run through the entire img with .jumpFwd(1), an ArrayIndexOutOfBoundsException is thrown.

Greetings, Squareys

Timeline for removing deprecated classes

Some classes in imglib2-algorithm are labeled deprecated, e.g. net.imglib2.algorithm.gauss.Gauss. Do we have a timeline for when they should be removed? This should certainly happen before a 1.0.0 release as the zero major version still indicates a fluid API. My thought on this is that this should happen sooner rather than later because multiple classes (some of which are deprecated) with similar functionality can be confusing for callers, for example #59. Also, the sooner deprecated methods disappear, the sooner people stop building their software on top of them.

Seeded watersheds implementation for grayscale and affinity images

I have an implementation of the seeded watersheds transform at
https://github.com/hanslovsky/imglib2-algorithm/tree/watersheds-cleanup
comparison with master
The code is pretty clean and documentation will be added before a potential pull request. Right now, I have two concerns:

  • Added dependency for primitive type queues:
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
<version>7.0.12</version>
</dependency>

Please comment on the added dependency issue and feel free to discuss the need for watersheds on unbounded RandomAccessible.

Convenience methods for signed distance transform

With the newly introduced binary distance transform methods (#69, #71), we can now very easily create a signed distance transform (beware: Kotlin!):

private fun <B: BooleanType<B>, T: RealType<T>> signedDistanceTransform(
		mask: RandomAccessibleInterval<B>,
		distanceOutside: RandomAccessibleInterval<T>,
		distanceInside: RandomAccessibleInterval<T>,
		vararg weights: Double = doubleArrayOf(1.0),
		distanceType: DistanceTransform.DISTANCE_TYPE = DistanceTransform.DISTANCE_TYPE.EUCLIDIAN
): RandomAccessibleInterval<T> {
	DistanceTransform.binaryTransform(mask, distanceOutside, distanceType, *weights)
	DistanceTransform.binaryTransform(not(mask), distanceInside, distanceType, *weights)
	val paired= Views.interval(Views.pair(distanceOutside, distanceInside), mask)
	return difference(paired)

}

with helper methods

fun <B: BooleanType<B>> not(mask: RandomAccessibleInterval<B>): RandomAccessibleInterval<B>
{
	return Converters.convert(mask, { s,t -> t.set(!s.get()) }, Util.getTypeFromInterval(mask).createVariable())!!
}

fun<T: RealType<T>> difference(pairs: RandomAccessibleInterval<Pair<T, T>>): RandomAccessibleInterval<T> {
	return Converters.convert(
			pairs,
			{ s,t -> t.set(s.a); t.sub(s.b) },
			Util.getTypeFromInterval(pairs).a.createVariable())!!
}

This will be negative inside the object defined by mask and positive everywhere else.

Improve DistanceTransform usability

@tischi recently contacted me about using DistanceTransform and had a few questions about it. After helping him out we compiled a short list of improvements that would make calling DistanceTransform easier:

  • Documentation should clearly state that Euclidian distance is actually squared Euclidian distance.
  • There should be convenience methods for binary distance transform (converting true to zero and false to positive infinity)
  • Information about distance transform from sampled functions should be added to each individual overload (not only in class doc) so callers can see it when they auto-complete in eclipse
  • add comment that weights in squared Euclidian transform are not being squared (while coordinates are), i.e. callers need to pass squared weights, e.g. to adjust for pixel anisotropy

Cannot use Gauss3 in Spark cluster environment

Sample error message:

Exception in thread "main" org.apache.spark.SparkException: Job aborted due to stage failure: Task 64 in stage 0.0 failed 4 times, most recent failure: Lost task 64.3 in stage 0.0 (TID 180, 10.36.110.14, executor 11): java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.NoClassDefFoundErro
        at net.imglib2.algorithm.convolution.LineConvolution.execute(LineConvolution.java:151)
        at net.imglib2.algorithm.convolution.LineConvolution.forEachIntervalElementInParallel(LineConvolution.java:135)
        at net.imglib2.algorithm.convolution.LineConvolution.process(LineConvolution.java:83)
        at net.imglib2.algorithm.convolution.AbstractMultiThreadedConvolution.process(AbstractMultiThreadedConvolution.java:55)
        at net.imglib2.algorithm.convolution.Concatenation.process(Concatenation.java:80)
        at net.imglib2.algorithm.gauss3.Gauss3.gauss(Gauss3.java:207)
        at net.imglib2.algorithm.gauss3.Gauss3.gauss(Gauss3.java:131)
        at net.imglib2.algorithm.gauss3.Gauss3.gauss(Gauss3.java:92)
        at org.janelia.saalfeldlab.label.spark.affinities.SparkRain.smooth(SparkRain.java:757)
        at org.janelia.saalfeldlab.label.spark.affinities.SparkRain.access$600(SparkRain.java:79)
        at org.janelia.saalfeldlab.label.spark.affinities.SparkRain$CropAffinities.call(SparkRain.java:823)
        at org.janelia.saalfeldlab.label.spark.affinities.SparkRain$CropAffinities.call(SparkRain.java:782)
        at org.apache.spark.api.java.JavaPairRDD$$anonfun$pairFunToScalaFun$1.apply(JavaPairRDD.scala:1043)
        at org.apache.spark.api.java.JavaPairRDD$$anonfun$pairFunToScalaFun$1.apply(JavaPairRDD.scala:1043)
        at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
        at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
        at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
        at scala.collection.Iterator$class.foreach(Iterator.scala:893)
        at scala.collection.AbstractIterator.foreach(Iterator.scala:1336)
        at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:59)
        at scala.collection.mutable.ArrayBuffer.$plus$plus$eq(ArrayBuffer.scala:104)
        at scala.collection.mutable.ArrayBuffer.$plus$plus$eq(ArrayBuffer.scala:48)
        at scala.collection.TraversableOnce$class.to(TraversableOnce.scala:310)
        at scala.collection.AbstractIterator.to(Iterator.scala:1336)
        at scala.collection.TraversableOnce$class.toBuffer(TraversableOnce.scala:302)
        at scala.collection.AbstractIterator.toBuffer(Iterator.scala:1336)
        at scala.collection.TraversableOnce$class.toArray(TraversableOnce.scala:289)
        at scala.collection.AbstractIterator.toArray(Iterator.scala:1336)
        at org.apache.spark.rdd.RDD$$anonfun$collect$1$$anonfun$12.apply(RDD.scala:939)
        at org.apache.spark.rdd.RDD$$anonfun$collect$1$$anonfun$12.apply(RDD.scala:939)
        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:2074)
        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:2074)
        at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
        at org.apache.spark.scheduler.Task.run(Task.scala:109)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:345)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.util.concurrent.ExecutionException: java.lang.NoClassDefFoundError: net/imglib2/algorithm/convolution/kernel/Kernel1D
        at java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.util.concurrent.FutureTask.get(FutureTask.java:192)
        at net.imglib2.algorithm.convolution.LineConvolution.execute(LineConvolution.java:144)

Might be related to imglib/imglib2#241 and imglib/imglib2#234

Morphology framework TODOs.

After the merge of PR #2, the following tasks were noted to be done:

  • RectangleShape and CenteredRectangleShape should be unified and support for non-symmetric neighborhood should be added. This can all be the same class with a bunch of more constructors.
  • The xxxNeighborhoodLocalizableSampler classes share a lot of code. There is potential for unification there. We could make xxxNeighborhoodFactory implementations non-static and have them contain all the parameters required to construct the neighborhoods. Then there could be only one NeighborhoodFactory and one generic typed NeighborhoodLocalizableSampler that is used everywhere.
  • The xxxShape.NeighborhoodsAccessible.randomAccess( final Interval interval ) should make an effort to compute the interval of the source that is required (i.e., output interval plus border depending on the strel bounding box) and request a source RandomAccess for that required interval. This will allow Views to make better choices of whether it can strip away extensions in transform chains. Also in some cases it is required to actually enforce the extension for extended-and-then-cropped views. See javadoc added in imglib/imglib2@3569f58 for additional explanation. (I (Tobias) would suggest to postpone this until after the previous item has been addressed because all required logic is in the RectangleNeighborhoodLocalizableSampler already.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.