Пример #1
0
        public MyPipeline(HSBuffer <byte> inBuf)
        {
            Input = inBuf;
            // For this lesson, we'll use a two-stage pipeline that sharpens
            // and then applies a look-up-table (LUT).

            // First we'll define the LUT. It will be a gamma curve.

            Lut[I] = HS.Cast <byte>(HS.Clamp(HSMath.Pow(I / 255.0f, 1.2f) * 255.0f, 0, 255));

            // Augment the input with a boundary condition.
            Padded[X, Y, C] = Input[HS.Clamp(X, 0, Input.Width - 1),
                                    HS.Clamp(Y, 0, Input.Height - 1), C];

            // Cast it to 16-bit to do the math.
            Padded16[X, Y, C] = HS.Cast <ushort>(Padded[X, Y, C]);

            // Next we sharpen it with a five-tap filter.
            Sharpen[X, Y, C] = (Padded16[X, Y, C] * 2 -
                                (Padded16[X - 1, Y, C] +
                                 Padded16[X, Y - 1, C] +
                                 Padded16[X + 1, Y, C] +
                                 Padded16[X, Y + 1, C]) / 4);

            // Then apply the LUT.
            Curved[X, Y, C] = Lut[Sharpen[X, Y, C]];
        }
Пример #2
0
        public static int Main(string[] args)
        {
            // First we'll declare some Vars to use below.
            var x = new HSVar("x");
            var y = new HSVar("y");
            var c = new HSVar("c");

            // Now we'll express a multi-stage pipeline that blurs an image
            // first horizontally, and then vertically.
            {
                // Take a color 8-bit input
                var input = HSBuffer <byte> .LoadImage("rgb.png");

                // Upgrade it to 16-bit, so we can do math without it overflowing.
                var input_16 = new HSFunc("input_16");
                input_16[x, y, c] = HS.Cast <ushort>(input[x, y, c]);

                // Blur it horizontally:
                var blur_x = new HSFunc("blur_x");
                blur_x[x, y, c] = (input_16[x - 1, y, c] +
                                   2 * input_16[x, y, c] +
                                   input_16[x + 1, y, c]) / 4;

                // Blur it vertically:
                var blur_y = new HSFunc("blur_y");
                blur_y[x, y, c] = (blur_x[x, y - 1, c] +
                                   2 * blur_x[x, y, c] +
                                   blur_x[x, y + 1, c]) / 4;

                // Convert back to 8-bit.
                var output = new HSFunc("output");
                output[x, y, c] = HS.Cast <byte>(blur_y[x, y, c]);

                // Each Func in this pipeline calls a previous one using
                // familiar function call syntax (we've overloaded operator()
                // on Func objects). A Func may call any other Func that has
                // been given a definition. This restriction prevents
                // pipelines with loops in them. Halide pipelines are always
                // feed-forward graphs of Funcs.

                // Now let's realize it...

                // Buffer<byte> result = output.realize(input.width(), input.height(), 3);

                // Except that the line above is not going to work. Uncomment
                // it to see what happens.

                // Realizing this pipeline over the same domain as the input
                // image requires reading pixels out of bounds in the input,
                // because the blur_x stage reaches outwards horizontally, and
                // the blur_y stage reaches outwards vertically. Halide
                // detects this by injecting a piece of code at the top of the
                // pipeline that computes the region over which the input will
                // be read. When it starts to run the pipeline it first runs
                // this code, determines that the input will be read out of
                // bounds, and refuses to continue. No actual bounds checks
                // occur in the inner loop; that would be slow.
                //
                // So what do we do? There are a few options. If we realize
                // over a domain shifted inwards by one pixel, we won't be
                // asking the Halide routine to read out of bounds. We saw how
                // to do this in the previous lesson:
                var result = new HSBuffer <byte>(input.Width - 2, input.Height - 2, 3);
                result.SetMin(1, 1);
                output.Realize(result);

                // Save the result. It should look like a slightly blurry
                // parrot, and it should be two pixels narrower and two pixels
                // shorter than the input image.
                result.SaveImage("blurry_parrot_1.png");

                // This is usually the fastest way to deal with boundaries:
                // don't write code that reads out of bounds :) The more
                // general solution is our next example.
            }

            // The same pipeline, with a boundary condition on the input.
            {
                // Take a color 8-bit input
                var input = HSBuffer <byte> .LoadImage("rgb.png");

                // This time, we'll wrap the input in a Func that prevents
                // reading out of bounds:
                var clamped = new HSFunc("clamped");

                // Define an expression that clamps x to lie within the
                // range [0, input.width()-1].
                var clamped_x = HS.Clamp(x, 0, input.Width - 1);
                // clamp(x, a, b) is equivalent to max(min(x, b), a).

                // Similarly clamp y.
                var clamped_y = HS.Clamp(y, 0, input.Height - 1);
                // Load from input at the clamped coordinates. This means that
                // no matter how we evaluated the Func 'clamped', we'll never
                // read out of bounds on the input. This is a clamp-to-edge
                // style boundary condition, and is the simplest boundary
                // condition to express in Halide.
                clamped[x, y, c] = input[clamped_x, clamped_y, c];

                // Defining 'clamped' in that way can be done more concisely
                // using a helper function from the BoundaryConditions
                // namespace like so:
                //
                // clamped = BoundaryConditions::repeat_edge(input);
                //
                // These are important to use for other boundary conditions,
                // because they are expressed in the way that Halide can best
                // understand and optimize. When used correctly they are as
                // cheap as having no boundary condition at all.

                // Upgrade it to 16-bit, so we can do math without it
                // overflowing. This time we'll refer to our new Func
                // 'clamped', instead of referring to the input image
                // directly.
                var input_16 = new HSFunc("input_16");
                input_16[x, y, c] = HS.Cast <ushort>(clamped[x, y, c]);

                // The rest of the pipeline will be the same...

                // Blur it horizontally:
                var blur_x = new HSFunc("blur_x");
                blur_x[x, y, c] = (input_16[x - 1, y, c] +
                                   2 * input_16[x, y, c] +
                                   input_16[x + 1, y, c]) / 4;

                // Blur it vertically:
                var blur_y = new HSFunc("blur_y");
                blur_y[x, y, c] = (blur_x[x, y - 1, c] +
                                   2 * blur_x[x, y, c] +
                                   blur_x[x, y + 1, c]) / 4;

                // Convert back to 8-bit.
                var output = new HSFunc("output");
                output[x, y, c] = HS.Cast <byte>(blur_y[x, y, c]);

                // This time it's safe to evaluate the output over the some
                // domain as the input, because we have a boundary condition.
                var result = output.Realize <byte>(input.Width, input.Height, 3);

                // Save the result. It should look like a slightly blurry
                // parrot, but this time it will be the same size as the
                // input.
                result.SaveImage("blurry_parrot_2.png");
            }

            Console.WriteLine("Success!");
            return(0);
        }
Пример #3
0
        public static int Main(string[] args)
        {
            // This program defines a single-stage imaging pipeline that
            // brightens an image.

            // First we'll load the input image we wish to brighten.
            var input = HSBuffer <byte> .LoadImage("rgb.png");

            // See figures/lesson_02_input.jpg for a smaller version.

            // Next we define our Func object that represents our one pipeline
            // stage.
            var brighter = new HSFunc("brighter");

            // Our Func will have three arguments, representing the position
            // in the image and the color channel. Halide treats color
            // channels as an extra dimension of the image.
            var x = new HSVar("x");
            var y = new HSVar("y");
            var c = new HSVar("c");

            // Normally we'd probably write the whole function definition on
            // one line. Here we'll break it apart so we can explain what
            // we're doing at every step.

            // For each pixel of the input image.
            var value = input[x, y, c];

            // Cast it to a floating point value.
            value = HS.Cast <float>(value);

            // Multiply it by 1.5 to brighten it. Halide represents real
            // numbers as floats, not doubles, so we stick an 'f' on the end
            // of our constant.
            value = value * 1.5f;

            // Clamp it to be less than 255, so we don't get overflow when we
            // cast it back to an 8-bit unsigned int.
            value = HSMath.Min(value, 255.0f);

            // Cast it back to an 8-bit unsigned integer.
            value = HS.Cast <byte>(value);

            // Define the function.
            brighter[x, y, c] = value;

            // The equivalent one-liner to all of the above is:
            //
            // brighter(x, y, c) = Halide::cast<uint8_t>(min(input(x, y, c) * 1.5f, 255));
            //
            // In the shorter version:
            // - I skipped the cast to float, because multiplying by 1.5f does
            //   that automatically.
            // - I also used an integer constant as the second argument in the
            //   call to min, because it gets cast to float to be compatible
            //   with the first argument.
            // - I left the Halide:: off the call to min. It's unnecessary due
            //   to Koenig lookup.

            // Remember, all we've done so far is build a representation of a
            // Halide program in memory. We haven't actually processed any
            // pixels yet. We haven't even compiled that Halide program yet.

            // So now we'll realize the Func. The size of the output image
            // should match the size of the input image. If we just wanted to
            // brighten a portion of the input image we could request a
            // smaller size. If we request a larger size Halide will throw an
            // error at runtime telling us we're trying to read out of bounds
            // on the input image.
            var output =
                brighter.Realize <byte>(input.Width, input.Height, input.Channels);

            // Save the output for inspection. It should look like a bright parrot.
            output.SaveImage("brighter.png");

            // See figures/lesson_02_output.jpg for a small version of the output.

            Console.WriteLine("Success!");
            return(0);
        }