Article Purpose
This article is focussed on illustrating the steps required in performing an image Shear Transformation. All of the concepts explored have been implemented by means of raw pixel data processing, no conventional drawing methods, such as GDI, are required.
Rabbit: Shear X 0.4, Y 0.4
Sample Source Code
This article is accompanied by a sample source code Visual Studio project which is available for download here.
Using the Sample Application
This article features a Windows Forms based sample application which is included as part of the accompanying sample source code. The concepts explored in this article can be illustrated in a practical implementation using the sample application.
The sample application enables a user to load source/input images from the local file system when clicking the Load Image button. In addition users are also able to save output result images to the local file system by clicking the Save Image button.
Image Shear Transformations can be applied to either X or Y, or both X and Y pixel coordinates. When using the sample application the user has option of adjusting Shear factors, as indicated on the user interface by the numeric up/down controls labelled Shear X and Shear Y.
The following image is a screenshot of the Image Transform Shear Sample Application in action:
Rabbit: Shear X -0.5, Y -0.25
Image Shear Transformation
A good definition of the term Shear Transformation can be found on the Wikipedia topic related article:
In plane geometry, a shear mapping is a linear map that displaces each point in fixed direction, by an amount proportional to its signed distance from a line that is parallel to that direction.[1] This type of mapping is also called shear transformation, transvection, or just shearing
A Shear Transformation can be applied as a horizontal shear, a vertical shear or as both. The algorithms implemented when performing a Shear Transformation can be expressed as follows:
Horizontal Shear Algorithm
Vertical Shear Algorithm
The algorithm description:
- Shear(x) : The result of a horizontal Shear Transformation – The calculated X-Coordinate representing a Shear Transform.
- Shear(y) : The result of a vertical Shear Transformation – The calculated Y-Coordinate representing a Shear Transform.
- σ : The lower case version of the Greek alphabet letter Sigma – Represents the Shear Factor.
- x : The X-Coordinate originating from the source/input image – The horizontal coordinate value intended to be sheared.
- y : The Y-Coordinate originating from the source/input image – The vertical coordinate value intended to be sheared.
- H : Source image height in pixels.
- W : Source image width in pixels.
Note: When performing a Shear Transformation implementing both the horizontal and vertical planes each coordinate plane can be calculated using a different shearing factor.
The algorithms have been adapted in order to implement a middle pixel offset by means of subtracting the product of the related image plane boundary and the specified Shearing Factor, which will then be divided by a factor of two.
Rabbit: Shear X 1.0, Y 0.1
Implementing a Shear Transformation
The sample source code performs Shear Transformations through the implementation of the extension methods ShearXY and ShearImage.
The ShearXY extension method targets the Point structure. The algorithms discussed in the previous sections have been implemented in this function from a C# perspective. The definition as illustrated by the following code snippet:
public static Point ShearXY(this Point source, double shearX, double shearY, int offsetX, int offsetY) { Point result = new Point();
result.X = (int)(Math.Round(source.X + shearX * source.Y)); result.X -= offsetX;
result.Y = (int)(Math.Round(source.Y + shearY * source.X)); result.Y -= offsetY;
return result; }
Rabbit: Shear X 0.0, Y 0.5
The ShearImage extension method targets the Bitmap class. This method expects as parameter values a horizontal and a vertical shearing factor. Providing a shearing factor of zero results in no image shearing being implemented in the corresponding direction. The definition as follows:
public static Bitmap ShearImage(this Bitmap sourceBitmap, double shearX, double shearY) { BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
int xOffset = (int )Math.Round(sourceBitmap.Width * shearX / 2.0);
int yOffset = (int )Math.Round(sourceBitmap.Height * shearY / 2.0);
int sourceXY = 0; int resultXY = 0;
Point sourcePoint = new Point(); Point resultPoint = new Point();
Rectangle imageBounds = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
for (int row = 0; row < sourceBitmap.Height; row++) { for (int col = 0; col < sourceBitmap.Width; col++) { sourceXY = row * sourceData.Stride + col * 4;
sourcePoint.X = col; sourcePoint.Y = row;
if (sourceXY >= 0 && sourceXY + 3 < pixelBuffer.Length) { resultPoint = sourcePoint.ShearXY(shearX, shearY, xOffset, yOffset);
resultXY = resultPoint.Y * sourceData.Stride + resultPoint.X * 4;
if (imageBounds.Contains(resultPoint) && resultXY >= 0) { if (resultXY + 6 <= resultBuffer.Length) { resultBuffer[resultXY + 4] = pixelBuffer[sourceXY];
resultBuffer[resultXY + 5] = pixelBuffer[sourceXY + 1];
resultBuffer[resultXY + 6] = pixelBuffer[sourceXY + 2];
resultBuffer[resultXY + 7] = 255; }
if (resultXY - 3 >= 0) { resultBuffer[resultXY - 4] = pixelBuffer[sourceXY];
resultBuffer[resultXY - 3] = pixelBuffer[sourceXY + 1];
resultBuffer[resultXY - 2] = pixelBuffer[sourceXY + 2];
resultBuffer[resultXY - 1] = 255; }
if (resultXY + 3 < resultBuffer.Length) { resultBuffer[resultXY] = pixelBuffer[sourceXY];
resultBuffer[resultXY + 1] = pixelBuffer[sourceXY + 1];
resultBuffer[resultXY + 2] = pixelBuffer[sourceXY + 2];
resultBuffer[resultXY + 3] = 255; } } } } }
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap; }
Rabbit: Shear X 0.5, Y 0.0
Sample Images
This article features a number of sample images. All featured images have been licensed allowing for reproduction.
The sample images featuring the image of a Desert Cottontail Rabbit is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license and can be downloaded from Wikipedia. The original author is attributed as Larry D. Moore.
The sample images featuring the image of a Rabbit in Snow is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license and can be downloaded from Wikipedia. The original author is attributed as George Tuli.
The sample images featuring the image of an Eastern Cottontail Rabbit has been released into the public domain by its author. The original image can be downloaded from Wikipedia.
The sample images featuring the image of a Mountain Cottontail Rabbit is in the public domain in the United States because it is a work prepared by an officer or employee of the United States Government as part of that person’s official duties under the terms of Title 17, Chapter 1, Section 105 of the US Code. The original image can be downloaded from Wikipedia.
Rabbit: Shear X 1.0, Y 0.0
Rabbit: Shear X 0.5, Y 0.1
Rabbit: Shear X -0.5, Y -0.25
Rabbit: Shear X -0.5, Y 0.0
Rabbit: Shear X 0.25, Y 0.0
Rabbit: Shear X 0.50, Y 0.0
Rabbit: Shear X 0.0, Y 0.5
Rabbit: Shear X 0.0, Y 0.25
Rabbit: Shear X 0.0, Y 1.0
Related Articles and Feedback
Feedback and questions are always encouraged. If you know of an alternative implementation or have ideas on a more efficient implementation please share in the comments section.
I’ve published a number of articles related to imaging and images of which you can find URL links here:
- C# How to: Image filtering by directly manipulating Pixel ARGB values
- C# How to: Image filtering implemented using a ColorMatrix
- C# How to: Blending Bitmap images using colour filters
- C# How to: Bitmap Colour Substitution implementing thresholds
- C# How to: Generating Icons from Images
- C# How to: Swapping Bitmap ARGB Colour Channels
- C# How to: Bitmap Pixel manipulation using LINQ Queries
- C# How to: Linq to Bitmaps – Partial Colour Inversion
- C# How to: Bitmap Colour Balance
- C# How to: Bi-tonal Bitmaps
- C# How to: Bitmap Colour Tint
- C# How to: Bitmap Colour Shading
- C# How to: Image Solarise
- C# How to: Image Contrast
- C# How to: Bitwise Bitmap Blending
- C# How to: Image Arithmetic
- C# How to: Image Convolution
- C# How to: Image Edge Detection
- C# How to: Difference Of Gaussians
- C# How to: Image Median Filter
- C# How to: Image Unsharp Mask
- C# How to: Image Colour Average
- C# How to: Image Erosion and Dilation
- C# How to: Morphological Edge Detection
- C# How to: Boolean Edge Detection
- C# How to: Gradient Based Edge Detection
- C# How to: Sharpen Edge Detection
- C# How to: Calculating Gaussian Kernels
- C# How to: Image Blur
- C# How to: Image Transform Rotate
- C# How to: Compass Edge Detection
- C# How to: Oil Painting and Cartoon Filter
- C# How to: Stained Glass Image Filter