Skip to content

Commit 942a235

Browse files
committed
docs: add explanation and examples for projective transformation
1 parent b7c5d56 commit 942a235

File tree

3 files changed

+88
-72
lines changed

3 files changed

+88
-72
lines changed

docs/Tutorials/Applying transform function on images.md

Lines changed: 88 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
1-
## Synopsis
2-
31
Image transformations are fundamental operations in computer graphics and image processing that allow you to manipulate the position, size, shape, and perspective of images. This tutorial covers both affine and projective transformations, providing practical examples and clear explanations of how each parameter affects your image.
42

53
![Affine transformations](./images/transformations/affine-transform.gif);
64

7-
## Understanding Transformation Types
8-
95
In this tutorial, we distinguish between two primary types of transformations:
106

117
### Affine Transformations
128

139
- **Preserve**: Collinearity and ratios of distances
1410
- **Properties**: Parallel lines remain parallel, straight lines remain straight
1511
- **Use cases**: Scaling, rotation, translation, shearing
16-
- **Matrix size**: 2×3 (the bottom row [0, 0, 1] is implied)
12+
- **Matrix size**: 2×3(the bottom row [0, 0, 1] is implied)
1713

1814
### Projective Transformations
1915

2016
- **Preserve**: Only collinearity (straight lines remain straight)
2117
- **Properties**: Parallel lines may converge, creates perspective effects
2218
- **Use cases**: Perspective correction, 3D projections, keystone correction
2319
- **Matrix size**: 3×3 (full matrix required)
24-
- **The key difference**: affine transformations might stretch, rotate, or shift a rectangle, but parallel lines remain parallel. Projective transformations can make a rectangle appear tilted or receding into the distance, with parallel lines converging to vanishing points.
20+
21+
**The key difference** is that affine transformations might stretch, rotate, or shift a rectangle, but parallel lines remain parallel. Projective transformations can make a rectangle appear tilted or receding into the distance, with parallel lines converging to vanishing points.
2522

2623
## The Transformation Matrix
2724

@@ -43,21 +40,13 @@ Each parameter controls specific aspects of the transformation:
4340
- `g`, `h`: Perspective distortion
4441
- `i`: Normalization factor (usually 1)
4542

46-
## Getting Started
47-
48-
First, let's load an image:
49-
50-
```ts
51-
import { readSync } from 'image-processing-library';
52-
53-
const image = readSync('/path/to/image.png');
54-
```
43+
For affine transformation 2x3 matrix will be used, because last row is not necessary for this kind of transformation.
5544

5645
## Affine Transformations
5746

5847
### Scaling
5948

60-
Scaling changes the size of your image. Parameters a and e control horizontal and vertical scaling respectively.
49+
Scaling changes the size of your image. Parameters `a` and `e` control horizontal and vertical scaling respectively.
6150

6251
```ts
6352
// Scale image by factor of 2 (Maintaining Aspect Ratio)
@@ -93,6 +82,7 @@ const shrinkMatrix = [
9382
[0.5, 0, 0],
9483
[0, 0.5, 0],
9584
];
85+
const shrunkImage = image.transform(shrinkMatrix);
9686
```
9787

9888
![Shrunk image](./images/transformations/lennaShrunk.png)
@@ -103,6 +93,7 @@ const mirrorMatrix = [
10393
[-1, 0, 0],
10494
[0, 1, 0],
10595
];
96+
const mirrorredImage = image.transform(mirrorMatrix);
10697
```
10798

10899
![Mirrored image](./images/transformations/lennaMirrorred.png)
@@ -113,6 +104,7 @@ const flipMatrix = [
113104
[1, 0, 0],
114105
[0, -1, 0],
115106
];
107+
const flippedImage = image.transform(flipMatrix);
116108
```
117109

118110
![Flipped image](./images/transformations/lennaFlipped.png)
@@ -157,107 +149,115 @@ const rotatedImage = image.transform(rotationMatrix);
157149

158150
![Rotated image](./images/transformations/lennaRotated.png)
159151

160-
### Rotation Around Image Center
161-
162-
To rotate around the image center instead of the origin, combine translation with rotation:
163-
164-
```ts
165-
const angle = Math.PI / 4; //45 degrees
166-
const center = image.getCoordinates('center');
167-
168-
const cos = Math.cos(angle);
169-
const sin = Math.sin(angle);
170-
171-
// Translate to origin, rotate, translate back
172-
const matrix = [
173-
[cos, -sin, center.column * (1 - cos) + center.row * sin],
174-
[sin, cos, center.row * (1 - cos) - center.column * sin],
175-
];
176-
177-
return image.transform(matrix);
178-
```
179-
180-
:::note
181-
Image-js has functions `rotate()` and `transformRotate()`. `rotate()` function allows rotating an image by multiple of 90 degrees.
182-
`transformRotate()` allows rotating an image by any degree. It also allows choosing the axe of rotation. So, for rotation, you have other functions that allow you to perform it.
183-
Current tutorial just demonstrates the basic principle behind transformation of such kind.
184-
:::
185-
186-
![Rotated by center image](./images/transformations/lennaRotatedCenter.png)
187-
188152
### Shearing
189153

190-
Shearing skews the image, making rectangles appear as parallelograms. Parameters b and d control shearing.
154+
Shearing skews the image, making rectangles appear as parallelograms. Parameters `b` and `d` control shearing.
155+
156+
#### Horizontal shearing
191157

192158
```ts
193159
// Horizontal shear - lean the image to the right
194160
const horizontalShearMatrix = [
195161
[1, 0.5, 0], // b=0.5 creates horizontal shear
196162
[0, 1, 0],
197163
];
164+
165+
const horizontalShearImage = image.transform(horizontalShearMatrix);
198166
```
199167

200168
![Horizontally sheared image](./images/transformations/lennaHorizontalShear.png)
201169

170+
#### Vertical shearing
171+
202172
```ts
203173
// Vertical shear - lean the image upward
204174
const verticalShearMatrix = [
205175
[1, 0, 0],
206176
[0.3, 1, 0], // d=0.3 creates vertical shear
207177
];
178+
179+
const verticalShearImage = image.transform(vertcialShearMatrix);
208180
```
209181

210182
![Vertically sheared image](./images/transformations/lennaVerticalShear.png)
211183

184+
#### Combined shearing
185+
212186
```ts
213187
// Combined shearing
214188
const combinedShearMatrix = [
215189
[1, 0.5, 0], // Horizontal shear
216190
[0.3, 1, 0], // Vertical shear
217191
];
192+
193+
const combinedShearImage = image.transform(combinedShearMatrix);
218194
```
219195

220196
![Combined shearing](./images/transformations/lennaCombinedShear.png)
221197

222198
### Complex Affine Transformations
223199

224200
You can combine multiple transformations by multiplying matrices or applying them sequentially:
201+
For example, to rotate around the image center instead of the origin, combine translation with rotation:
225202

226203
```ts
227-
// Scale, rotate, and translate in one transformation
228-
const angle = Math.PI / 4;
229-
const scale = 1.5;
230-
const translateX = 100;
231-
const translateY = 50;
232-
233-
const complexMatrix = [
234-
[scale * Math.cos(angle), -scale * Math.sin(angle), translateX],
235-
[scale * Math.sin(angle), scale * Math.cos(angle), translateY],
204+
const angle = Math.PI / 4; //45 degrees
205+
const center = image.getCoordinates('center');
206+
207+
const cos = Math.cos(angle);
208+
const sin = Math.sin(angle);
209+
210+
// Translate to origin, rotate, translate back
211+
const matrix = [
212+
[cos, -sin, center.column * (1 - cos) + center.row * sin],
213+
[sin, cos, center.row * (1 - cos) - center.column * sin],
236214
];
237215

238-
const complexTransform = image.transform(complexMatrix);
216+
return image.transform(matrix);
239217
```
240218

219+
![Rotated by center image](./images/transformations/lennaRotatedCenter.png)
220+
221+
:::note
222+
Image-js has functions `rotate()` and `transformRotate()`. `rotate()` function allows rotating an image by multiple of 90 degrees.
223+
`transformRotate()` allows rotating an image by any degree. It also allows choosing the axe of rotation. So, for rotation, you have other functions that allow you to perform it.
224+
Current tutorial just demonstrates the basic principle behind transformation of such kind.
225+
:::
226+
241227
## Projective Transformations
242228

243229
Projective transformations use the full 3×3 matrix, including the bottom row parameters `g`, `h`, and `i`. These create perspective effects and can map rectangular images onto quadrilaterals.
244230

245231
### Understanding Perspective Parameters
246232

247-
`g`, `h`: Control perspective distortion
248-
`i`: Normalization factor (typically 1)
233+
- `g`, `h`: control horizontal and vertical perspective distortion.
234+
- `i`: Normalization factor (typically 1). It simulates what happens in real vision:
235+
236+
1. `i` < 1: Objects farther away appear smaller
237+
2. `i` > 1: Objects closer appear larger
238+
3. The division by w' mathematically recreates this effect
239+
240+
The normalization factor is essentially artificial depth - it makes flat 2D coordinates behave as if they exist in 3D space with varying distances from the viewer.
241+
The distortion allows modifying the angle under which you look at the image.
249242

250243
```ts
251-
// Simple perspective transformation
252244
const perspectiveMatrix = [
253-
[1, 0, 0], // Standard scaling and translation
245+
[1, 0, 0],
254246
[0, 1, 0],
255-
[0.001, 0, 1], // g=0.001 creates horizontal perspective
247+
[0.001, -0.0002, 1],
256248
];
257-
258-
const perspectiveImage = image.transform(perspectiveMatrix);
249+
const perspectiveImage = newImage.transform(perspectiveMatrix, {
250+
//using fullImage:true to show all the pixels.
251+
fullImage: true,
252+
// using bicubic interpolation for a more detailed image.
253+
interpolationType: 'bicubic',
254+
});
259255
```
260256

257+
![Perspective image](./images/transformations/buildingsPerspective.png)
258+
259+
Now, let's take a look at practical uses of such transformation.
260+
261261
### Four-Point Mapping
262262

263263
The most common use of projective transformation is mapping an image to fit within four corner points:
@@ -267,7 +267,7 @@ The most common use of projective transformation is mapping an image to fit with
267267
```ts
268268
const image = readSync('path/to/file.png');
269269
// Define source corners (original image points) and destination image width and height.
270-
// Width and height can be omitted. In this case width and height will be calculated from points.
270+
// In this case they correspond to credit card's corner points.
271271
const sourcePoints = [
272272
[
273273
{ column: 55, row: 140 },
@@ -289,22 +289,38 @@ const projectedImage = image.transform(matrix.matrix, {
289289

290290
![Corrected perspective](./images/transformations/card-perspectiveWarp.png);
291291

292+
:::note
293+
For calculating perspective warp matrix, width and height can be omitted as options.
294+
In that case, they will be calculated from source points.
295+
:::
296+
292297
### Keystone Correction
293298

294-
Correcting perspective distortion (like photographing a screen at an angle). Let's take this image
295-
as an example.
299+
Let's take this image again as an example.
296300

297301
![Keystone image](./images/transformations/buildings.jpg)
298302

299-
A common problem when taking photos of tall buildings is that they can look as if they're leaning backwards. This is known as the "keystone effect" (or the "tombstone effect"), and it can be a very distracting form of distortion in your images.
303+
A common problem when taking photos of tall buildings is that they can look as if they're leaning backwards. This is known as the "[keystone effect](https://en.wikipedia.org/wiki/Keystone_effect)" (or the "tombstone effect"), and it can be a very distracting form of distortion in your images.
300304

301305
```ts
302-
// Correct keystone effect - make trapezoid into rectangle
303-
const keystoneMatrix = [
304-
[1.2, 0.1, -50],
305-
[0.05, 1.1, -20],
306-
[0.0002, 0.0001, 1],
307-
];
306+
// Correcting keystone effect - make trapezoid into rectangle. These points work for an image with
307+
//buildings. For more automatic approach you need to use something more advanced.
308+
const keystoneMatrix = getPerspectiveWarp([
309+
{ column: 60, row: 0 },
310+
{ column: newImage.width - 59, row: 0 },
311+
{ column: newImage.width - 1, row: newImage.height - 1 },
312+
{ column: 0, row: newImage.height - 1 },
313+
]);
314+
315+
const correctedImage = newImage.transform(keystoneMatrix.matrix, {
316+
inverse: true,
317+
width: newImage.width,
318+
height: newImage.height,
319+
// Using bicubic interpolation for better image quality.
320+
interpolationType: 'bicubic',
321+
});
308322

309323
const correctedImage = image.transform(keystoneMatrix);
310324
```
325+
326+
![Corrected keystone effect](./images/transformations/buildingsCorrected.png)
Loading
Loading

0 commit comments

Comments
 (0)