Beispiel #1
0
        public override VideoFrame GetFrame(int n, ScriptEnvironment env)
        {
            var frame = Child.GetFrame(n, env);

            env.MakeWritable(frame);
            using (var sampleFrame = sampleClip.GetFrame(n, env))
                using (var referenceFrame = referenceClip.GetFrame(n, env))
                    using (var maskFrame = maskClip?.GetFrame(n, env))
                    {
                        var pixelSize = sampleClip.GetVideoInfo().IsRGB24() ? 3 : 1;
                        //for (var channel = 0; channel < pixelSize; channel++)
                        //var planes = sampleClip.GetVideoInfo().IsRGB24()
                        //    ? new[] {default(YUVPlanes)}
                        //    : new[] {YUVPlanes.PLANAR_Y, YUVPlanes.PLANAR_U, YUVPlanes.PLANAR_V};
                        var planes = new[] { default(YUVPlanes) };
                        Parallel.ForEach(planes, plane =>
                        {
                            Parallel.For(0, pixelSize, channel =>
                            {
                                var sampleHist    = GetHistogram(sampleFrame, maskFrame, pixelSize, channel, plane);
                                var referenceHist = GetHistogram(referenceFrame, maskFrame, pixelSize, channel, plane);
                                var map           = GetTransitionMap(sampleHist, referenceHist);
#if DEBUG
                                System.Diagnostics.Debug.WriteLine($"Channel {channel}: ");
                                for (var i = 0; i < 256; i++)
                                {
                                    //System.Diagnostics.Debug.WriteLine($"{i}: {sampleHist[i]} - {map.Select((newColor, oldColor) => new { newColor, oldColor }).Where(p => p.newColor == i).Sum(p => sampleHist[p.oldColor])} - {referenceHist[i]}");
                                    System.Diagnostics.Debug.WriteIf(i != map[i], $"{i} -> {map[i]}, ");
                                }
                                System.Diagnostics.Debug.WriteLine("");
#endif
                                unsafe
                                {
                                    var data = (byte *)frame.GetWritePtr();
                                    for (var y = 0;
                                         y < frame.GetHeight();
                                         y++, data += frame.GetPitch())
                                    {
                                        for (var x = 0; x < frame.GetRowSize(); x += pixelSize)
                                        {
                                            data[x] = map[data[x]];
                                        }
                                    }
                                }
                            });
                        });
                    }
            return(frame);
        }
        public override VideoFrame GetFrame(int n, ScriptEnvironment env)
        {
            // This is the function that AviSynth calls to get a given frame.
            // So when this functions gets called, the filter is supposed to return frame n.

            VideoFrame src = Child.GetFrame(n, env);
            // Request frame 'n' from the child (source) clip.
            VideoFrame window = WindowVideo.GetFrame(n, env);
            // Request frame "'n" from the WindowVideo clip
            VideoFrame dst = NewVideoFrame(env);
            // Construct a frame based on the information of the current frame.

            Stream srcp = src.GetReadStream();
            // Request a Read pointer from the source frame.
            // This will return the position of the upperleft pixel in YUY2 images,
            // and return the lower-left pixel in RGB.
            // RGB images are stored upside-down in memory.
            // You should still process images from line 0 to height.
            // Note: In this sample we use Stream class to manipulate the buffer,
            //       although you can still use pointer in C#.

            Stream dstp = dst.GetWriteStream();
            // Request a Write pointer from the newly created destination image.
            // You can request a writepointer to images that have just been
            // created by NewVideoFrame. If you recieve a frame from Clip.GetFrame(...)
            // you must call env.MakeWritable(frame) be recieve a valid write pointer.

            int dst_pitch = dst.GetPitch();
            // Requests pitch (length of a line) of the destination image.
            // For more information on pitch see: http://www.avisynth.org/index.php?page=WorkingWithImages
            // (short version - pitch is always equal to or greater than width to allow for seriously fast assembly code)

            int dst_width = dst.GetRowSize();
            // Requests rowsize (number of used bytes in a line.
            // See the link above for more information.

            int dst_height = dst.GetHeight();
            // Requests the height of the destination image.

            int src_pitch  = src.GetPitch();
            int src_width  = src.GetRowSize();
            int src_height = src.GetHeight();

            Stream windowp       = window.GetReadStream();
            int    window_pitch  = window.GetPitch();
            int    window_width  = window.GetRowSize();
            int    window_height = window.GetHeight();
            // Get info on the Windowed Clip (see src definitions for more information)


            int w, h;

            // This version of SimpleSample is intended to show how to utilise information from 2 clips in YUY2
            // colourspace only.  The original V1.6 code has been left in place fro all other
            // colourspaces.
            // It is designed purely for clarity and not as good or clever code :-)


            if (vi.IsRGB24())
            {
                // The code just deals with RGB24 colourspace where each pixel is represented by
                // 3 bytes, Blue, Green and Red.
                // Although this colourspace is the easiest to understand, it is very rarely used because
                // a 3 byte sequence (24bits) cannot be processed easily using normal 32 bit registers.

                for (h = 0; h < src_height; h++)         // Loop from bottom line to top line.
                {
                    for (w = 0; w < src_width; w += 3)   // Loop from left side of the image to the right side 1 pixel (3 bytes) at a time
                                                         // stepping 3 bytes (a pixel width in RGB24 space)

                    {
                        dstp.WriteByte((byte)srcp.ReadByte());     // Copy each Blue byte from source to destination.
                        dstp.WriteByte((byte)srcp.ReadByte());     // Copy Green.
                        dstp.WriteByte((byte)srcp.ReadByte());     // Copy Red

                        //Note: The code above is for demonstration only. You should copy 1 line
                        //      at a time for better performance. The same applies to codes below.
                    }

                    srcp.Seek(src_pitch - src_width, SeekOrigin.Current); // Seek to next line.
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
                // end copy src to dst

                //Now draw a white square in the middle of the frame
                // Normally you'd do this code within the loop above but here it is in a separate loop for clarity;

                dstp.Seek((dst_height / 2 - SquareSize / 2) * dst_pitch, SeekOrigin.Begin); // move pointer to SquareSize/2 lines from the middle of the frame;
                for (h = 0; h < SquareSize; h++)                                            // only scan SquareSize lines
                {
                    dstp.Seek(dst_width / 2 - SquareSize / 2 * 3, SeekOrigin.Current);      // seek to left edge of the square
                    for (w = 0; w < SquareSize; w += 1)                                     // only scans the middle SquareSize pixels of a line
                    {
                        dstp.WriteByte(255);                                                // Set Blue to maximum value.
                        dstp.WriteByte(255);                                                // and Green.
                        dstp.WriteByte(255);                                                // and Red - therefore the whole pixel is now white.
                    }
                    dstp.Seek(dst_width / 2 - SquareSize / 2 * 3, SeekOrigin.Current);      //seek to right edge of the frame
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);                   //seek to next line
                }
            }

            if (vi.IsRGB32())
            {
                // This code deals with RGB32 colourspace where each pixel is represented by
                // 4 bytes, Blue, Green and Red and "spare" byte that could/should be used for alpha
                // keying but usually isn't.

                // Although this colourspace isn't memory efficient, code end ups running much
                // quicker than RGB24 as you can deal with whole 32bit variables at a time
                // and easily work directly and quickly in assembler (if you know how to that is :-)
                byte[] buffer = new byte[4];
                for (h = 0; h < src_height; h++)           // Loop from bottom line to top line.
                {
                    for (w = 0; w < src_width / 4; w += 1) // and from leftmost pixel to rightmost one.
                    {
                        srcp.Read(buffer, 0, 4);
                        dstp.Write(buffer, 0, 4);                         // Copy each whole pixel from source to destination.
                    }
                    srcp.Seek(src_pitch - src_width, SeekOrigin.Current); // Seek to next line.
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
                // end copy src to dst

                //Now draw a white square in the middle of the frame
                // Normally you'd do this code within the loop above but here it is in a separate loop for clarity;

                dstp.Seek((dst_height / 2 - SquareSize / 2) * dst_pitch, SeekOrigin.Begin); // move pointer to SquareSize/2 lines from the middle of the frame;

                int woffset = dst_width / 8 - SquareSize / 2;                               // lets precalulate the width offset like we do for the lines.

                buffer = BitConverter.GetBytes((int)0x00FFFFFF);                            // initialize the buffer with white pixel
                                                                                            // LSB = Blue, MSB = "spare" byte
                for (h = 0; h < SquareSize; h++)                                            // only scan SquareSize number of lines
                {
                    dstp.Seek(woffset * 4, SeekOrigin.Current);
                    for (w = 0; w < SquareSize; w += 1)   // only scans the middle SquareSize pixels of a line
                    {
                        dstp.Write(buffer, 0, 4);
                    }
                    dstp.Seek(woffset * 4, SeekOrigin.Current);
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
            }

            if (vi.IsYUY2())
            {
                // This code deals with YUY2 colourspace where each 4 byte sequence represents
                // 2 pixels, (Y1, U, Y2 and then V).

                // This colourspace is more memory efficient than RGB32 but can be more awkward to use sometimes.
                // However, it can still be manipulated 32bits at a time depending on the
                // type of filter you are writing

                // There is no difference in code for this loop and the RGB32 code due to a coincidence :-)
                // 1) YUY2 frame_width is half of an RGB32 one
                // 2) But in YUY2 colourspace, a 32bit variable holds 2 pixels instead of the 1 in RGB32 colourspace.
                byte[] buffer = new byte[4];
                for (h = 0; h < src_height; h++)           // Loop from top line to bottom line (opposite of RGB colourspace).
                {
                    for (w = 0; w < src_width / 4; w += 1) // and from leftmost double-pixel to rightmost one.
                    {
                        srcp.Read(buffer, 0, 4);
                        dstp.Write(buffer, 0, 4);                         // Copy 2 pixels worth of information from source to destination.
                    }                                                     // at a time by temporarily treating the src and dst pointers as
                                                                          // 32bit (4 byte) pointers intead of 8 bit (1 byte) pointers
                    srcp.Seek(src_pitch - src_width, SeekOrigin.Current); // Seek to next line.
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
                // end copy src to dst

                //Now draw the other clip inside a square in the middle of the frame
                // Normally you'd do this code within the loop above but here it is in a separate loop for clarity;

                dstp.Seek((dst_height / 2 - SquareSize / 2) * dst_pitch, SeekOrigin.Begin);  // move pointer to SquareSize/2 lines from the middle of the frame;

                windowp = window.GetReadStream();

                int woffset = dst_width / 8 - SquareSize / 4; // lets precalulate the width offset like we do for the lines.
                for (h = 0; h < SquareSize; h++)              // only scan SquareSize number of lines
                {
                    dstp.Seek(woffset * 4, SeekOrigin.Current);
                    for (w = 0; w < SquareSize / 2; w += 1)   // only scans the middle SquareSize pixels of a line

                    {
                        windowp.Read(buffer, 0, 4);
                        dstp.Write(buffer, 0, 4);                                    // Pixels to come from top left of WindowVideo
                    }
                    windowp.Seek(window_pitch - SquareSize * 2, SeekOrigin.Current); // Seek to next line.
                    dstp.Seek(woffset * 4, SeekOrigin.Current);
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
            }

            if (vi.IsYV12())
            {
                // This code deals with YV12 colourspace where the Y, U and V information are
                // stored in completely separate memory areas

                // This colourspace is the most memory efficient but usually requires 3 separate loops
                // However, it can actually be easier to deal with than YUY2 depending on your filter algorithim

                // So first of all deal with the Y Plane

                for (h = 0; h < src_height; h++)                          // Loop from top line to bottom line (Sames as YUY2.
                {
                    for (w = 0; w < src_width; w++)                       // Loop from left side of the image to the right side.
                    {
                        dstp.WriteByte((byte)srcp.ReadByte());            // Copy each byte from source to destination.
                    }
                    srcp.Seek(src_pitch - src_width, SeekOrigin.Current); // Seek to next line.
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
                // end copy Y Plane src to dst

                //Now set the Y plane bytes to maximum in the middle of the frame
                // Normally you'd do this code within the loop above but here it is in a separate loop for clarity;

                dstp.Seek((dst_height / 2 - SquareSize / 2) * dst_pitch, SeekOrigin.Begin); // move pointer to SquareSize/2 lines from the middle of the frame;

                int woffset = dst_width / 2 - SquareSize / 2;                               // lets precalulate the width offset like we do for the lines.

                for (h = 0; h < SquareSize; h++)                                            // only scan SquareSize number of lines
                {
                    dstp.Seek(woffset, SeekOrigin.Current);
                    for (w = 0; w < SquareSize; w += 1) // only scans the middle SquareSize pixels of a line
                    {
                        dstp.WriteByte(235);            // Set Y values to maximum
                    }
                    dstp.Seek(woffset, SeekOrigin.Current);
                    dstp.Seek(dst_pitch - dst_width, SeekOrigin.Current);
                }
                // end of Y plane Code

                // This section of code deals with the U and V planes of planar formats (e.g. YV12)
                // So first of all we have to get the additional info on the U and V planes

                int dst_pitchUV  = dst.GetPitch(YUVPlanes.PLANAR_U);   // The pitch,height and width information
                int dst_widthUV  = dst.GetRowSize(YUVPlanes.PLANAR_U); // is guaranted to be the same for both
                int dst_heightUV = dst.GetHeight(YUVPlanes.PLANAR_U);  // the U and V planes so we only the U
                int src_pitchUV  = src.GetPitch(YUVPlanes.PLANAR_U);   // plane values and use them for V as
                int src_widthUV  = src.GetRowSize(YUVPlanes.PLANAR_U); // well
                int src_heightUV = src.GetHeight(YUVPlanes.PLANAR_U);  //

                //Copy U plane src to dst
                srcp = src.GetReadStream(YUVPlanes.PLANAR_U);
                dstp = dst.GetWriteStream(YUVPlanes.PLANAR_U);

                for (h = 0; h < src_heightUV; h++)
                {
                    for (w = 0; w < src_widthUV; w++)
                    {
                        dstp.WriteByte((byte)srcp.ReadByte());
                    }
                    ;
                    srcp.Seek(src_pitchUV - src_widthUV, SeekOrigin.Current);
                    dstp.Seek(dst_pitchUV - dst_widthUV, SeekOrigin.Current);
                }
                // end copy U plane src to dst

                //Now set the U plane bytes to no colour in the middle of the frame
                // Normally you'd do this code within the loop above but here it is in a separate loop for clarity;

                // reset the destination pointer to the top, left pixel.
                dstp.Seek((dst_heightUV / 2 - SquareSize / 4) * dst_pitchUV, SeekOrigin.Begin); // note change in how much we dived SquareSize by
                                                                                                // as the U plane height is half the Y plane

                woffset = dst_widthUV / 2 - SquareSize / 4;                                     // And the divisor changes here as well compared to Y plane code.

                for (h = 0; h < SquareSize / 2; h++)                                            // only scan SquareSize/2 number of lines (because the U plane height is half the Y)
                {
                    dstp.Seek(woffset, SeekOrigin.Current);
                    for (w = 0; w < SquareSize / 2; w += 1) // only scans the middle SquareSize/2 bytes of a line because ... U=Y/2 :-)
                    {
                        dstp.WriteByte(128);                // Set U Value to no colour
                    }
                    dstp.Seek(woffset, SeekOrigin.Current);
                    dstp.Seek(dst_pitchUV - dst_widthUV, SeekOrigin.Current);
                }
                // end of U plane Code



                //Copy V plane src to dst
                srcp = src.GetReadStream(YUVPlanes.PLANAR_V);
                dstp = dst.GetWriteStream(YUVPlanes.PLANAR_V);

                for (h = 0; h < src_heightUV; h++)
                {
                    for (w = 0; w < src_widthUV; w++)
                    {
                        dstp.WriteByte((byte)srcp.ReadByte());
                    }
                    ;
                    srcp.Seek(src_pitchUV - src_widthUV, SeekOrigin.Current);
                    dstp.Seek(dst_pitchUV - dst_widthUV, SeekOrigin.Current);
                }
                // end copy V plane src to dst

                //Now set the V plane bytes to no colour in the middle of the frame
                // the code is identical to the code for U plane apart from getting the frame start pointer.
                // Normally you'd do this code within the loop above but here it is in a separate loop for clarity;

                dstp.Seek((dst_heightUV / 2 - SquareSize / 4) * dst_pitchUV, SeekOrigin.Begin); // note change in how much we dived SquareSize by
                                                                                                // as the V plane height is half the Y plane

                woffset = dst_widthUV / 2 - SquareSize / 4;                                     // And the divisor changes here as well compared to Y plane code.

                for (h = 0; h < SquareSize / 2; h++)                                            // only scan SquareSize/2 number of lines (because the V plane height is half the Y)
                {
                    dstp.Seek(woffset, SeekOrigin.Current);
                    for (w = 0; w < SquareSize / 2; w += 1) // only scans the middle SquareSize/2 bytes of a line because ... U=Y/2 :-)
                    {
                        dstp.WriteByte(128);                // Set V Value to no colour
                    }
                    dstp.Seek(woffset, SeekOrigin.Current);
                    dstp.Seek(dst_pitchUV - dst_widthUV, SeekOrigin.Current);
                }
                // end of U plane Code
            }

            //Release all VideoFrames except for the destination frame.
            //Important! If you don't do that, your memory will quickly run out.
            src.Dispose();
            window.Dispose();

            // As we now are finished processing the image, we return the destination image.
            return(dst);
        }