1
1
2
2
/**
3
- * @file Helper module for image processing.
4
- *
5
- * These functions and classes are only used internally,
3
+ * @file Helper module for image processing.
4
+ *
5
+ * These functions and classes are only used internally,
6
6
* meaning an end-user shouldn't need to access anything here.
7
- *
7
+ *
8
8
* @module utils/image
9
9
*/
10
10
11
+ import { isNullishDimension } from './core.js' ;
11
12
import { getFile } from './hub.js' ;
12
13
import { env } from '../env.js' ;
13
14
import { Tensor } from './tensor.js' ;
@@ -91,7 +92,7 @@ export class RawImage {
91
92
this . channels = channels ;
92
93
}
93
94
94
- /**
95
+ /**
95
96
* Returns the size of the image (width, height).
96
97
* @returns {[number, number] } The size of the image (width, height).
97
98
*/
@@ -101,9 +102,9 @@ export class RawImage {
101
102
102
103
/**
103
104
* Helper method for reading an image from a variety of input types.
104
- * @param {RawImage|string|URL } input
105
+ * @param {RawImage|string|URL } input
105
106
* @returns The image object.
106
- *
107
+ *
107
108
* **Example:** Read image from a URL.
108
109
* ```javascript
109
110
* let image = await RawImage.read('https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/football-match.jpg');
@@ -181,7 +182,7 @@ export class RawImage {
181
182
182
183
/**
183
184
* Helper method to create a new Image from a tensor
184
- * @param {Tensor } tensor
185
+ * @param {Tensor } tensor
185
186
*/
186
187
static fromTensor ( tensor , channel_format = 'CHW' ) {
187
188
if ( tensor . dims . length !== 3 ) {
@@ -306,8 +307,8 @@ export class RawImage {
306
307
307
308
/**
308
309
* Resize the image to the given dimensions. This method uses the canvas API to perform the resizing.
309
- * @param {number } width The width of the new image.
310
- * @param {number } height The height of the new image.
310
+ * @param {number } width The width of the new image. `null` or `-1` will preserve the aspect ratio.
311
+ * @param {number } height The height of the new image. `null` or `-1` will preserve the aspect ratio.
311
312
* @param {Object } options Additional options for resizing.
312
313
* @param {0|1|2|3|4|5|string } [options.resample] The resampling method to use.
313
314
* @returns {Promise<RawImage> } `this` to support chaining.
@@ -324,6 +325,20 @@ export class RawImage {
324
325
// Ensure resample method is a string
325
326
let resampleMethod = RESAMPLING_MAPPING [ resample ] ?? resample ;
326
327
328
+ // Calculate width / height to maintain aspect ratio, in the event that
329
+ // the user passed a null value in.
330
+ // This allows users to pass in something like `resize(320, null)` to
331
+ // resize to 320 width, but maintain aspect ratio.
332
+ const nullish_width = isNullishDimension ( width ) ;
333
+ const nullish_height = isNullishDimension ( height ) ;
334
+ if ( nullish_width && nullish_height ) {
335
+ return this ;
336
+ } else if ( nullish_width ) {
337
+ width = ( height / this . height ) * this . width ;
338
+ } else if ( nullish_height ) {
339
+ height = ( width / this . width ) * this . height ;
340
+ }
341
+
327
342
if ( BROWSER_ENV ) {
328
343
// TODO use `resample` in browser environment
329
344
@@ -360,7 +375,7 @@ export class RawImage {
360
375
case 'nearest' :
361
376
case 'bilinear' :
362
377
case 'bicubic' :
363
- // Perform resizing using affine transform.
378
+ // Perform resizing using affine transform.
364
379
// This matches how the python Pillow library does it.
365
380
img = img . affine ( [ width / this . width , 0 , 0 , height / this . height ] , {
366
381
interpolator : resampleMethod
@@ -373,7 +388,7 @@ export class RawImage {
373
388
img = img . resize ( {
374
389
width, height,
375
390
fit : 'fill' ,
376
- kernel : 'lanczos3' , // PIL Lanczos uses a kernel size of 3
391
+ kernel : 'lanczos3' , // PIL Lanczos uses a kernel size of 3
377
392
} ) ;
378
393
break ;
379
394
@@ -452,7 +467,7 @@ export class RawImage {
452
467
// Create canvas object for this image
453
468
const canvas = this . toCanvas ( ) ;
454
469
455
- // Create a new canvas of the desired size. This is needed since if the
470
+ // Create a new canvas of the desired size. This is needed since if the
456
471
// image is too small, we need to pad it with black pixels.
457
472
const ctx = createCanvasFunction ( crop_width , crop_height ) . getContext ( '2d' ) ;
458
473
@@ -500,7 +515,7 @@ export class RawImage {
500
515
// Create canvas object for this image
501
516
const canvas = this . toCanvas ( ) ;
502
517
503
- // Create a new canvas of the desired size. This is needed since if the
518
+ // Create a new canvas of the desired size. This is needed since if the
504
519
// image is too small, we need to pad it with black pixels.
505
520
const ctx = createCanvasFunction ( crop_width , crop_height ) . getContext ( '2d' ) ;
506
521
0 commit comments