@@ -126,6 +126,17 @@ if ImageVision.ortex_configured?() do
126126 repo = Keyword . get ( options , :repo , @ default_repo )
127127 model_file = Keyword . get ( options , :model_file , @ default_model_file )
128128
129+ # `vips_thumbnail` (used inside `preprocess/1`) auto-applies
130+ # the EXIF `Orientation` tag, so the model receives the
131+ # upright image. The rest of this function operates on the
132+ # same upright frame so `scale_x` / `scale_y` and the box-
133+ # clamping `max_width` / `max_height` all agree. Without
134+ # this step, iPhone photos (stored as landscape pixels +
135+ # `Orientation = 6/8`) detect the face correctly but report
136+ # box coordinates that, when applied to the un-rotated
137+ # buffer, land elsewhere in the frame.
138+ image = Image . autorotate! ( image )
139+
129140 model = load_model ( repo , model_file )
130141
131142 { tensor , scale_x , scale_y } = preprocess ( image )
@@ -190,7 +201,9 @@ if ImageVision.ortex_configured?() do
190201 * `:padding` is a float in `[0.0, 5.0]` controlling how
191202 much room is kept around the face. `0.0` is a tight crop
192203 to the bounding box; `0.5` adds 50% on each side; `1.0`
193- doubles the bounding box. Default `0.2`.
204+ doubles the bounding box. Default `0.5` — matches the
205+ Cloudflare Images `face-zoom=0.5` default and tends to
206+ include shoulders for portrait-style photos.
194207
195208 ### Returns
196209
@@ -203,7 +216,13 @@ if ImageVision.ortex_configured?() do
203216 @ spec crop_largest ( image :: Vimage . t ( ) , options :: Keyword . t ( ) ) ::
204217 { :ok , Vimage . t ( ) } | { :error , :no_face_detected }
205218 def crop_largest ( % Vimage { } = image , options \\ [ ] ) do
206- padding = Keyword . get ( options , :padding , 0.2 )
219+ padding = Keyword . get ( options , :padding , 0.5 )
220+
221+ # Detection runs in the EXIF-rotated frame (see comment in
222+ # `detect/2`). The crop has to run in the same frame, so we
223+ # autorotate here too. Cheap no-op when the image has no
224+ # orientation tag or `Orientation = 1`.
225+ image = Image . autorotate! ( image )
207226 faces = detect ( image , options )
208227
209228 case largest_face ( faces ) do
0 commit comments