Esempio n. 1
0
        /// <summary>
        /// Draws a line from (-1, 1) to (1, 1) (transformed according to transform) as the base unit.
        /// </summary>
        /// <param name="transform">Coordinate transform to apply</param>
        /// <param name="image">Image upon which to render the line</param>
        /// <param name="color">Color to use</param>
        /// <param name="secondaryColor">Color to render onto auxilliary image (not used)</param>
        public static void renderLine(Matrix <double> transform, DirectBitmap image, int color, int secondaryColor)
        {
            Vector <double> pointA  = DenseVector.OfArray(new double[] { -1.0, 1.0, 1.0 });
            Vector <double> pointB  = DenseVector.OfArray(new double[] { 1.0, 1.0, 1.0 });
            Vector <double> pointAT = transform * pointA;
            Vector <double> pointBT = transform * pointB;

            ImProc.DrawLine(pointAT[0] / pointAT[2], pointAT[1] / pointAT[2], pointBT[0] / pointBT[2], pointBT[1] / pointBT[2], color, image);
        }
Esempio n. 2
0
        /// <summary>
        /// Renders the primary spirals of the fractal (transformed according to transform) as the base unit.
        /// NOTE: This will only render the spirals correctly if the transform is a product of
        /// rotations, translations and scalings (must scale both coordinates uniformly)
        /// </summary>
        /// <param name="transform">Coordinate transform to apply</param>
        /// <param name="image">Image upon which to render the spirals</param>
        /// <param name="color">Color to use</param>
        /// <param name="secondaryColor">Color to render onto auxilliary image (not used)</param>
        public static void renderPrimarySpirals(Matrix <double> transform, DirectBitmap image, int color, int secondaryColor)
        {
            Vector <double> pointA      = DenseVector.OfArray(new double[] { -1.0, 1.0, 1.0 });
            Vector <double> pointB      = DenseVector.OfArray(new double[] { 1.0, 1.0, 1.0 });
            Vector <double> pointAT     = transform * pointA;
            Vector <double> pointBT     = transform * pointB;
            double          scaleFactor = Math.Sqrt(transform.SubMatrix(0, 2, 0, 2).Determinant());
            Vector <double> xBasis      = DenseVector.OfArray(new double[] { 1.0, 0.0, 0.0 }); // make 3rd coordinate zero so translation is ignored
            Vector <double> xBasisT     = transform * xBasis;
            double          theta       = Math.Atan2(xBasisT[1], xBasisT[0]);

            ImProc.DrawSpiral(pointAT[0] / pointAT[2], pointAT[1] / pointAT[2], theta + Math.PI / 2, scaleFactor * 1.0 / 3.0, 6 * Math.PI, Math.PI / 2, Math.PI / 512, 1.0 / 16.0, color, image);
            ImProc.DrawSpiral(pointBT[0] / pointBT[2], pointBT[1] / pointBT[2], theta - Math.PI / 2, scaleFactor * 2.0 / 3.0, 6 * Math.PI, Math.PI / 2, Math.PI / 512, 1.0 / 16.0, color, image);
        }
Esempio n. 3
0
        /// <summary>
        /// Renders the secondary spirals of the fractal in blue (transformed according to transform) as the base unit.
        /// NOTE: This will only render the spirals correctly if the transform is a product of
        /// rotations, translations and scalings (must scale both coordinates uniformly)
        /// </summary>
        /// <param name="transform">Coordinate transform to apply</param>
        /// <param name="image">Image upon which to render the spirals</param>
        /// <param name="color">Color to use</param>
        /// <param name="color">Color to use</param>
        /// <param name="secondaryColor">Color to render onto auxilliary image</param>
        public void renderSecondarySpirals(Matrix <double> transform, DirectBitmap image, int color, int secondaryColor)
        {
            Vector <double> origin  = DenseVector.OfArray(new double[] { 0.0, 0.0, 1.0 });
            Vector <double> originT = transform * origin;
            // NOTE: Second spiral originating at pointB is not strictly necessary
            // (it will be constructed as a copy in another iteration). We only include
            // it so we can extend the theta bounds farther than they would otherwise go).
            Vector <double> pointB      = DenseVector.OfArray(new double[] { -0.5, 0.5, 1.0 });
            Vector <double> pointBT     = transform * pointB;
            double          scaleFactor = Math.Sqrt(transform.SubMatrix(0, 2, 0, 2).Determinant());
            Vector <double> xBasis      = DenseVector.OfArray(new double[] { 1.0, 0.0, 0.0 }); // make 3rd coordinate zero so translation is ignored
            Vector <double> xBasisT     = transform * xBasis;
            double          theta       = Math.Atan2(xBasisT[1], xBasisT[0]);
            // Suppress rendering if the origin is not a boundary pixel of the dragon fractal
            bool render = true;

            if (null != ReferenceImage)
            {
                int x = (int)(originT[0] / originT[2] + 0.5);
                int y = (int)(originT[1] / originT[2] + 0.5);
                if (x >= 0 && x < ReferenceImage.Width && y >= 0 && y < ReferenceImage.Height)
                {
                    if ((ReferenceImage.Bits[ReferenceImage.Width * y + x] & 0x00ffffff) == 0)
                    {
                        render = false;
                    }
                }
            }
            if (render)
            {
                double thetaPlusSpan, thetaMinusSpan;
                ImProc.DrawSpiral(originT[0] / originT[2], originT[1] / originT[2], theta + Math.PI, scaleFactor * 1.0 / 4.0, Math.PI / 512, 1.0 / 16.0, color, image, out thetaPlusSpan, out thetaMinusSpan);
                if (null != AuxImage && (thetaMinusSpan > 0 || thetaPlusSpan > 0))
                {
                    ImProc.DrawThickSpiral(originT[0] / originT[2], originT[1] / originT[2], theta + Math.PI, scaleFactor * 1.0 / 4.0, thetaPlusSpan, thetaMinusSpan, Math.PI / 512, 1.0 / 16.0, 0.1, secondaryColor, AuxImage);
                }
            }
        }
Esempio n. 4
0
        static void Main(string[] args)
        {
            string OUTPUT_DIRECTORY = @"C:\Users\Dianna2\data\DragonFractal";

            Directory.CreateDirectory(OUTPUT_DIRECTORY);
            double base_size      = (512.0 / 45.0);                                                     // base size is approximately 11.378 inches, fractal will be approximately 3 * base_size by 2 * base_size.
            double subImageWidth  = 9.0;                                                                // width of sub-images in inches
            double subImageHeight = 6.5;                                                                // height of sub-images in inches
            int    previewSF      = 16;                                                                 // Scale factor to use between the initial low-res preview and the final rull-res result. Must be a power of 2.

            double[]  toolRadii                = new double[] { 1 / 32.0, 1 / 16.0, 1 / 8.0, 1 / 4.0 }; // radii of the tools we have available in inches, ordered from smallest to largest
            const int N_ITER_PREVIEW           = 18;
            const int N_ITER_FINAL             = 26;
            const int N_ITER_SECONDARY_PREVIEW = 10;
            const int N_ITER_SECONDARY_FINAL   = 14;

            // Iterated function system defining the dragon fractal
            Fractal fractal = new Fractal();

            fractal.IFS = new List <Matrix <double> >();
            var Translate = DenseMatrix.OfArray(new double[, ] {
                { 1, 0, 1 }, { 0, 1, -1 }, { 0, 0, 1 }
            });
            var Scale = DenseMatrix.OfArray(new double[, ] {
                { Math.Sqrt(0.5), 0, 0 }, { 0, Math.Sqrt(0.5), 0 }, { 0, 0, 1 }
            });
            var Rot45 = DenseMatrix.OfArray(new double[, ] {
                { Math.Cos(Math.PI / 4), -Math.Sin(Math.PI / 4), 0 }, { Math.Sin(Math.PI / 4), Math.Cos(Math.PI / 4), 0 }, { 0, 0, 1 }
            });
            var Rot135 = DenseMatrix.OfArray(new double[, ] {
                { Math.Cos(3 * Math.PI / 4), -Math.Sin(3 * Math.PI / 4), 0 }, { Math.Sin(3 * Math.PI / 4), Math.Cos(3 * Math.PI / 4), 0 }, { 0, 0, 1 }
            });

            fractal.IFS.Add(Rot45 * Scale * Translate);
            fractal.IFS.Add(Rot135 * Scale * Translate);

            // x and y coordinates of the coffee table boundary polygon
            double[] xCoords = null, yCoords = null;

            // x and y coordinates of the (estimated) resin boundary polygon
            double[] xCoordsResin = null, yCoordsResin = null;

            for (int bigIter = 0; bigIter < 2; ++bigIter)
            {
                int sf             = (0 == bigIter ? 1 : previewSF) * 256; // note: needs to be a power of 2
                int DPI            = (int)Math.Round(sf / base_size);      // dots (pixels) per inch
                int pad            = (0 == bigIter ? 1 : previewSF) * 140;
                int w              = 3 * sf + pad;
                int h              = 2 * sf + pad;
                var finalTransform = DenseMatrix.OfArray(new double[, ] {
                    { sf, 0, 4 * sf / 3 + pad / 2 }, { 0, sf, sf / 3 + pad / 2 }, { 0, 0, 1 }
                });
                using (DirectBitmap image = new DirectBitmap(w, h),
                       borderImage = new DirectBitmap(w, h),
                       thickImage = new DirectBitmap(w, h))
                {
                    int white = unchecked ((int)0xffffffff); // white
                    int black = unchecked ((int)0xff000000); // black
                    int red   = unchecked ((int)0xffff0000); // red
                    int blue  = unchecked ((int)0xff0000ff); // blue
                                                             //int green = unchecked((int)0xff00ff00); // green

                    // Initialize the image with white
                    for (int y = 0; y < h; ++y)
                    {
                        for (int x = 0; x < w; ++x)
                        {
                            image.Bits[w * y + x]      = white;
                            thickImage.Bits[w * y + x] = white;
                        }
                    }

                    // Render fractal to N_ITER_PREVIEW/N_ITER_FINAL iterations, using a simple line segment as the base unit
                    fractal.RenderFractal(image, 0 == bigIter ? N_ITER_PREVIEW : N_ITER_FINAL, Fractal.renderLine, finalTransform, black, black);
                    ImProc.BWBoundary(image, borderImage, black); // compute boundary
                    fractal.ReferenceImage = borderImage;
                    fractal.AuxImage       = thickImage;

                    // Render the primary spirals of the fractal onto both image and thickImage
                    fractal.RenderFractal(image, 0, Fractal.renderPrimarySpirals, finalTransform, blue, black);
                    fractal.RenderFractal(thickImage, 0, Fractal.renderPrimarySpirals, finalTransform, black, black);

                    // Draw special thick spirals directly onto the thickImage
                    Vector <double> pointA      = DenseVector.OfArray(new double[] { -1.0, 1.0, 1.0 });
                    Vector <double> pointB      = DenseVector.OfArray(new double[] { 1.0, 1.0, 1.0 });
                    Vector <double> pointAT     = finalTransform * pointA;
                    Vector <double> pointBT     = finalTransform * pointB;
                    double          scaleFactor = Math.Sqrt(finalTransform.SubMatrix(0, 2, 0, 2).Determinant());
                    Vector <double> xBasis      = DenseVector.OfArray(new double[] { 1.0, 0.0, 0.0 }); // make 3rd coordinate zero so translation is ignored
                    Vector <double> xBasisT     = finalTransform * xBasis;
                    double          theta       = Math.Atan2(xBasisT[1], xBasisT[0]);
                    for (int i = 0; i < 12; ++i)
                    {
                        ImProc.DrawTaperedSpiral(pointAT[0] / pointAT[2], pointAT[1] / pointAT[2], theta + Math.PI / 4 + i * Math.PI / 4, scaleFactor * Math.Sqrt(2.0) * Math.Pow(0.5, 0.5 * i) / 3.0, 0, Math.PI / 4, Math.PI / 1000, 1.0 / 16.0, 0.02, 1.0, black, thickImage);
                        ImProc.DrawTaperedSpiral(pointBT[0] / pointBT[2], pointBT[1] / pointBT[2], theta - 3 * Math.PI / 4 + i * Math.PI / 4, scaleFactor * Math.Sqrt(2.0) * Math.Pow(0.5, 0.5 * i) * 2.0 / 3.0, 0, Math.PI / 4, Math.PI / 1000, 1.0 / 16.0, 0.02, 1.0, black, thickImage);
                    }

                    int nIterSS = 0 == bigIter ? N_ITER_SECONDARY_PREVIEW : N_ITER_SECONDARY_FINAL; // number of iterations of the secondary spirals to compute
                    // Render iterations of the secondary spirals onto the fractal
                    for (int i = 0; i < nIterSS; ++i)
                    {
                        fractal.RenderFractal(image, i, fractal.renderSecondarySpirals, finalTransform, blue, black);
                    }

                    // Clean up the white diagonal lines
                    ImProc.BWFilter(thickImage, ImProc.CleanWhiteDiagonalsLUT);

                    IO.SaveImage(image, Path.Combine(OUTPUT_DIRECTORY, "thinfractal" + bigIter.ToString() + ".bmp"));
                    IO.SaveImage(borderImage, Path.Combine(OUTPUT_DIRECTORY, "fractalborder" + bigIter.ToString() + ".bmp"));

                    if (0 == bigIter)
                    {
                        // Dilate fractal with a circular filter of radius 60
                        byte[,] maskImage = ImProc.ToBWByteNeq(image, white); // mask indicates where original image is non-white
                        bool[,] kernel    = ImProc.GenerateDiscKernel(60);
                        int kernelRadius = (kernel.GetLength(0) - 1) / 2;
                        maskImage          = ImProc.BWDilate(maskImage, kernel, kernelRadius, kernelRadius);
                        byte[,] maskImage2 = new byte[h, w];
                        ImProc.BWBoundary4(maskImage, maskImage2, 255);
                        maskImage = null;
                        // Detect the contour
                        ImProc.FindClosedContour(maskImage2, out xCoords, out yCoords);
                        // Smooth the contour
                        double   sigma         = 5.0;
                        int      halfWidth     = (int)Math.Ceiling(3.0 * sigma);
                        double[] gaussKernel   = ImProc.ConstructGaussianKernel(sigma, halfWidth, true);
                        double[] xCoordsSmooth = ImProc.Convolve1D(xCoords, gaussKernel, 1, -1, 1);
                        double[] yCoordsSmooth = ImProc.Convolve1D(yCoords, gaussKernel, 1, -1, 1);
                        // Decimate smoothed curves so curvature based smoothing will work faster
                        int N  = xCoords.Length;
                        int N2 = N / 10;
                        xCoords = new double[N2];
                        yCoords = new double[N2];
                        for (int i = 0; i < N2; ++i)
                        {
                            xCoords[i] = xCoordsSmooth[(int)(i * N / (double)N2 + 0.5)];
                            yCoords[i] = yCoordsSmooth[(int)(i * N / (double)N2 + 0.5)];
                        }
                        // Apply further curvature-based smoothing
                        ImProc.CurvatureSmoothContour(xCoords, yCoords, 2000.0, 1.0, 10.0, true);

                        // Estimate resin boundary
                        maskImage    = ImProc.ToBWByteNeq(image, white);         // mask indicates where original image is non-white
                        kernel       = ImProc.GenerateDiscKernel((DPI * 3) / 8); // Filter radius = 3/8 inch
                        kernelRadius = (kernel.GetLength(0) - 1) / 2;
                        maskImage    = ImProc.BWDilate(maskImage, kernel, kernelRadius, kernelRadius);
                        //maskImage2 = new byte[h, w];
                        List <Tuple <int, int> > seeds = new List <Tuple <int, int> >();
                        seeds.Add(Tuple.Create(0, 0));
                        List <Tuple <int, int> > contour;
                        ImProc.BWSelect(maskImage, out maskImage2, seeds, 0, 255, 0, out contour);
                        maskImage = new byte[h, w];
                        ImProc.BWBoundary4(maskImage2, maskImage, 255);
                        maskImage2 = null;
                        // Detect the contour
                        ImProc.FindClosedContour(maskImage, out xCoordsResin, out yCoordsResin);
                        // Smooth the contour
                        sigma         = 5.0;
                        halfWidth     = (int)Math.Ceiling(3.0 * sigma);
                        gaussKernel   = ImProc.ConstructGaussianKernel(sigma, halfWidth, true);
                        xCoordsSmooth = ImProc.Convolve1D(xCoordsResin, gaussKernel, 1, -1, 1);
                        yCoordsSmooth = ImProc.Convolve1D(yCoordsResin, gaussKernel, 1, -1, 1);
                        // Decimate smoothed curves so curvature based smoothing will work faster
                        N            = xCoordsResin.Length;
                        N2           = N / 10;
                        xCoordsResin = new double[N2];
                        yCoordsResin = new double[N2];
                        for (int i = 0; i < N2; ++i)
                        {
                            xCoordsResin[i] = xCoordsSmooth[(int)(i * N / (double)N2 + 0.5)];
                            yCoordsResin[i] = yCoordsSmooth[(int)(i * N / (double)N2 + 0.5)];
                        }
                    }
                    else
                    {
                        // Scale contour by a factor of previewSF
                        int N = xCoords.Length;
                        for (int i = 0; i < N; ++i)
                        {
                            xCoords[i] = xCoords[i] * previewSF;
                            yCoords[i] = yCoords[i] * previewSF;
                        }

                        N = xCoordsResin.Length;
                        for (int i = 0; i < N; ++i)
                        {
                            xCoordsResin[i] = xCoordsResin[i] * previewSF;
                            yCoordsResin[i] = yCoordsResin[i] * previewSF;
                        }
                    }

                    // Clear borderImage for more drawing operations
                    ImProc.SetColor(borderImage, black); // Comment out this line to include the fractal boundary in the final image

                    // Add the boundaries of the region(s) reachable by each tool
                    for (int t = 0; t < toolRadii.Length; ++t)
                    {
                        // Open the fractal with a disc shaped kernel of radius toolRadii[t],
                        // adding this boundary to the original boundary. This will show
                        // us how far a tool of radius toolRadii[t] can make it into the spirals.
                        byte[,] tempMask = ImProc.ToBWByteEq(thickImage, black);
                        bool[,] kernel   = ImProc.GenerateDiscKernel(DPI * toolRadii[t]);
                        int kernelRadius = (kernel.GetLength(0) - 1) / 2;
                        tempMask = ImProc.BWOpen(tempMask, kernel, kernelRadius, kernelRadius);
                        ImProc.BWBoundary(tempMask, borderImage, 255);
                        GC.Collect(); // free up memory we temporarily allocated
                    }

                    // Dilate the blue region in the first image with a disc shaped kernel of radius of toolRadii[0]
                    // Then draw these pixels in black onto the thickImage. This will make the spirals of the thick image
                    // never get thinner than our smallest tool.
                    {
                        byte[,] tempMask = ImProc.ToBWByteEq(image, blue);
                        bool[,] kernel   = ImProc.GenerateDiscKernel(DPI * toolRadii[0]);
                        int kernelRadius = (kernel.GetLength(0) - 1) / 2;
                        tempMask = ImProc.BWDilate(tempMask, kernel, kernelRadius, kernelRadius);
                        ImProc.DrawColor(thickImage, tempMask, black);
                    }
                    GC.Collect(); // free up memory we temporarily allocated

                    // Clean up the white diagonal lines
                    ImProc.BWFilter(thickImage, ImProc.CleanWhiteDiagonalsLUT);

                    IO.SaveImage(thickImage, Path.Combine(OUTPUT_DIRECTORY, "fractal" + bigIter.ToString() + ".bmp"));

                    // Extract and add boundary of thick fractal
                    ImProc.BWBoundary(thickImage, borderImage, black);

                    // Invert the image
                    ImProc.BWInvert(borderImage);

                    // Draw contour onto borderImage
                    ImProc.DrawClosedContour(xCoords, yCoords, black, borderImage);

                    // Draw resin contour onto borderImage, and save info (area enclosed by curve)
                    if (0 == bigIter)
                    {
                        ImProc.DrawClosedContour(xCoordsResin, yCoordsResin, red, borderImage);
                    }
                    string fractalInfoFilename = Path.Combine(OUTPUT_DIRECTORY, "fractalinfo" + bigIter.ToString() + ".txt");
                    using (StreamWriter sw = new StreamWriter(fractalInfoFilename))
                    {
                        double areaPixels = ImProc.MeasureArea(xCoords, yCoords);
                        sw.WriteLine("Total area enclosed by boundary = " + (areaPixels / (144.0 * DPI * DPI)).ToString() + " square feet");
                        areaPixels = ImProc.MeasureArea(xCoordsResin, yCoordsResin);
                        sw.WriteLine("Resin area = " + (areaPixels / (144.0 * DPI * DPI)).ToString() + " square feet");
                    }

                    IO.SaveImage(borderImage, Path.Combine(OUTPUT_DIRECTORY, "fractalwithboundary" + bigIter.ToString() + ".bmp"));

                    // Divide the image into sub-images and save them
                    if (1 == bigIter)
                    {
                        DirectBitmap[] subImages = ImProc.partitionImage(borderImage, (int)Math.Round(subImageWidth * DPI), (int)Math.Round(subImageHeight * DPI), black);
                        int            nImages   = subImages.Length;
                        for (int i = 0; i < nImages; ++i)
                        {
                            // See if we can find any non-white pixels in the interior of this image
                            bool done = false;
                            for (int y = 1; y < subImages[i].Height - 1; ++y)
                            {
                                for (int x = 1; x < subImages[i].Width - 1; ++x)
                                {
                                    if ((subImages[i].Bits[subImages[i].Width * y + x] & 0x00ffffff) != (white & 0x00ffffff))
                                    {
                                        done = true;
                                        break;
                                    }
                                }
                                if (done)
                                {
                                    break;
                                }
                            }
                            if (!done)
                            {
                                continue; // skip saving this image if we couldn't find any non-white pixels
                            }
                            using (Graphics graphics = Graphics.FromImage(subImages[i].Bitmap))
                            {
                                using (Font arialFont = new Font("Arial", 36))
                                {
                                    string text = i.ToString();
                                    // Measure string.
                                    SizeF stringSize = new SizeF();
                                    stringSize = graphics.MeasureString(text, arialFont);
                                    int hw = (int)Math.Ceiling(0.5 * (stringSize.Width + DPI / 2.0));
                                    int hh = (int)Math.Ceiling(0.5 * (stringSize.Height + DPI / 2.0));
                                    // Find a suitable location to put the text
                                    byte[,] tempMask = ImProc.ToBWByteEq(subImages[i], white);
                                    bool[,] kernel   = ImProc.GenerateRectKernel(2 * hw + 1, 1);
                                    tempMask         = ImProc.BWErode(tempMask, kernel, 1, hw);
                                    kernel           = ImProc.GenerateRectKernel(1, 2 * hh + 1);
                                    tempMask         = ImProc.BWErode(tempMask, kernel, hh, 1);
                                    // Find a pixel of tempMask near the center that is white
                                    int wi     = tempMask.GetLength(1);
                                    int hi     = tempMask.GetLength(0);
                                    int best_x = wi / 2;
                                    int best_y = hi / 2;
                                    done = false;
                                    for (int y = hi / 4; y < 3 * hi / 4; ++y)
                                    {
                                        for (int x = wi / 4; x < 3 * wi / 4; ++x)
                                        {
                                            if (tempMask[y, x] == 255)
                                            {
                                                best_x = x;
                                                best_y = y;
                                                done   = true;
                                                break;
                                            }
                                        }
                                        if (done)
                                        {
                                            break;
                                        }
                                    }
                                    // Draw the text onto the image
                                    graphics.DrawString(text, arialFont, Brushes.Black, best_x - 0.5f * stringSize.Width, best_y - 0.5f * stringSize.Height);
                                }
                            }

                            IO.SaveImage(subImages[i], Path.Combine(OUTPUT_DIRECTORY, "subimage" + i.ToString() + ".bmp"), DPI, DPI);
                        }
                    }
                }
            }
        }