@@ -16,7 +16,6 @@ public struct ProcessedFrames {
16
16
let totalDuration : CMTime
17
17
}
18
18
19
- // TODO: verify working color space, rendering color space
20
19
private let context = CIContext ( )
21
20
22
21
/// Collection of methods for processing media (images, video, etc.).
@@ -27,7 +26,7 @@ private let context = CIContext()
27
26
/// var image: CIImage
28
27
/// image = MediaProcessing.inSRGBToneCurveSpace(image)
29
28
///
30
- /// // apply user instructions
29
+ /// // Apply user instructions
31
30
/// image = MediaProcessing.apply(image, processing: processing)
32
31
///
33
32
/// image = MediaProcessing.resampleBicubic(image, to: config.size.cgSize)
@@ -76,58 +75,58 @@ public enum MediaProcessing {
76
75
return Float ( 1 / inputAspectRatio * desiredAspectRatio)
77
76
}
78
77
79
- /// Resample the image using bicubic interpolation.
80
- public static func resampleBicubic( _ image: CIImage , to size: CGSize ) -> CIImage {
81
- let filter = CIFilter . bicubicScaleTransform ( )
82
- let extent = image. extent. size
83
-
84
- filter. inputImage = image
85
-
86
- // set the aspect ratio to match the aspect ratio of the target
87
- filter. aspectRatio = aspectRatioForResample ( image, size: size)
88
-
89
- // that image is now the aspect ratio of the target and the size
90
- // of the shorter dimension
91
- let scale : CGFloat
92
- if extent. width < extent. height {
93
- scale = size. width / extent. width
94
- } else {
95
- scale = size. height / extent. height
96
- }
97
- filter. scale = Float ( scale)
98
-
99
- let rescaled = filter. outputImage!
100
-
101
- // the image has a DoD larger than the requested size so crop
102
- // it to the desired size
103
- return rescaled. cropped ( to: CGRect ( origin: . zero, size: size) )
104
- }
105
-
106
78
/// Resample the image using Lanczos interpolation.
107
79
static public func resampleLanczos( _ image: CIImage , to size: CGSize ) -> CIImage {
108
- let filter = CIFilter . lanczosScaleTransform ( )
109
- let extent = image. extent. size
80
+ // Create a bicubic scale filter
81
+
82
+ let yScale = size. height / image. extent. height
83
+ let xScale = size. width / image. extent. width
110
84
85
+ let filter = CIFilter . lanczosScaleTransform ( )
111
86
filter. inputImage = image
87
+ filter. scale = Float ( yScale)
88
+ filter. aspectRatio = Float ( xScale / yScale)
89
+ let scaledImage = filter. outputImage!
90
+
91
+ // Create a rect with the exact dimensions we want
92
+ let exactRect = CGRect (
93
+ x: 0 ,
94
+ y: 0 ,
95
+ width: size. width,
96
+ height: size. height
97
+ )
112
98
113
- // set the aspect ratio to match the aspect ratio of the target
114
- filter. aspectRatio = aspectRatioForResample ( image, size: size)
99
+ // Crop to ensure exact dimensions
100
+ return scaledImage. cropped ( to: exactRect)
101
+ }
115
102
116
- // that image is now the aspect ratio of the target and the size
117
- // of the shorter dimension
118
- let scale : CGFloat
119
- if extent. width < extent. height {
120
- scale = size. width / extent. width
121
- } else {
122
- scale = size. height / extent. height
123
- }
124
- filter. scale = Float ( scale)
103
+ /// Resample the image using bicubic interpolation.
104
+ /// - Parameters:
105
+ /// - image: The image to resample
106
+ /// - size: The target size
107
+ /// - Returns: The resampled image
108
+ public static func resampleBicubic( _ image: CIImage , to size: CGSize ) -> CIImage {
109
+ // Create a bicubic scale filter
110
+
111
+ let yScale = size. height / image. extent. height
112
+ let xScale = size. width / image. extent. width
125
113
126
- let rescaled = filter. outputImage!
114
+ let filter = CIFilter . bicubicScaleTransform ( )
115
+ filter. inputImage = image
116
+ filter. scale = Float ( yScale)
117
+ filter. aspectRatio = Float ( xScale / yScale)
118
+ let scaledImage = filter. outputImage!
119
+
120
+ // Create a rect with the exact dimensions we want
121
+ let exactRect = CGRect (
122
+ x: 0 ,
123
+ y: 0 ,
124
+ width: size. width,
125
+ height: size. height
126
+ )
127
127
128
- // the image has a DoD larger than the requested size so crop
129
- // it to the desired size
130
- return rescaled. cropped ( to: CGRect ( origin: . zero, size: size) )
128
+ // Crop to ensure exact dimensions
129
+ return scaledImage. cropped ( to: exactRect)
131
130
}
132
131
133
132
/// Normalize the image using the given mean and standard deviation parameters.
@@ -137,7 +136,7 @@ public enum MediaProcessing {
137
136
let filter = CIFilter . colorMatrix ( )
138
137
filter. inputImage = image
139
138
140
- // this should match
139
+ // This should match
141
140
// https://pytorch.org/vision/main/generated/torchvision.transforms.Normalize.html
142
141
//
143
142
// output[channel] = (input[channel] - mean[channel]) / std[channel]
@@ -156,6 +155,10 @@ public enum MediaProcessing {
156
155
}
157
156
158
157
/// Convert the CIImage into a planar 3 channel MLXArray `[1, C, H, W]`
158
+ /// - Parameters:
159
+ /// - image: The image to convert
160
+ /// - colorSpace: Optional color space for rendering
161
+ /// - Returns: The MLXArray representation of the image
159
162
public static func asMLXArray( _ image: CIImage , colorSpace: CGColorSpace ? = nil ) -> MLXArray {
160
163
let size = image. extent. size
161
164
let w = Int ( size. width. rounded ( ) )
@@ -178,10 +181,10 @@ public enum MediaProcessing {
178
181
179
182
var array = MLXArray ( data, [ h, w, 4 ] , type: Float32 . self)
180
183
181
- // drop 4th channel
184
+ // Drop 4th channel
182
185
array = array [ 0 ... , 0 ... , ..< 3 ]
183
186
184
- // convert to 1, C, H, W
187
+ // Convert to 1, C, H, W
185
188
array = array. reshaped ( 1 , h, w, 3 ) . transposed ( 0 , 3 , 1 , 2 )
186
189
187
190
return array
0 commit comments