@@ -1085,96 +1085,72 @@ MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
10851085%
10861086*/
10871087
1088- static double getDistance (const PointInfo * p ,const PointInfo * q ,
1089- const PointInfo * v )
1088+ typedef struct _CaliperInfo
10901089{
10911090 double
1092- gamma ;
1091+ area ,
1092+ width ,
1093+ height ,
1094+ projection ;
10931095
1094- /*
1095- Shortest distance between p to vector v through point q.
1096- */
1097- gamma = v -> y * PerceptibleReciprocal (v -> x );
1098- return (fabs (p -> y - (q -> y - gamma * q -> x )- gamma * p -> x )*
1099- PerceptibleReciprocal (sqrt (gamma * gamma + 1.0 )));
1100- }
1096+ ssize_t
1097+ p ,
1098+ q ,
1099+ v ;
1100+ } CaliperInfo ;
11011101
1102- static PointInfo getIntersection (const PointInfo * a ,const PointInfo * b ,
1103- const PointInfo * c ,const PointInfo * d )
1102+ static double getDistance (PointInfo * p ,PointInfo * q )
11041103{
1105- PointInfo
1106- m ,
1107- p ,
1108- vertex ;
1109-
1110- /*
1111- Intersection: b passing through a and d passing c.
1112- */
1113- m .x = b -> y * PerceptibleReciprocal (b -> x );
1114- p .x = a -> y - m .x * a -> x ;
1115- m .y = d -> y * PerceptibleReciprocal (d -> x );
1116- p .y = c -> y - m .y * c -> x ;
1117- vertex .x = (p .y - p .x )* PerceptibleReciprocal (m .x - m .y );
1118- vertex .y = m .x * vertex .x + p .x ;
1119- return (vertex );
1104+ return ((p -> x - q -> x )* (p -> x - q -> x )+ (p -> y - q -> y )* (p -> y - q -> y ));
11201105}
11211106
1122- static double getRadians ( const PointInfo * p ,const PointInfo * q )
1107+ static double getProjection ( PointInfo * p ,PointInfo * q , PointInfo * v )
11231108{
11241109 double
1125- gamma ;
1110+ distance ;
11261111
11271112 /*
1128- Angle in radians between vector p and q.
1113+ Projection of vector (x,y) - p into a line passing through p and q.
11291114 */
1130- gamma = sqrt (p -> x * p -> x + p -> y * p -> y )* sqrt (q -> x * q -> x + q -> y * q -> y );
1131- return (acos ((p -> x * q -> x + p -> y * q -> y )* PerceptibleReciprocal (gamma )));
1132- }
1133-
1134- static PointInfo * getVertex (PointInfo * vertices ,const ssize_t n ,
1135- const size_t number_vertices )
1136- {
1137- return (vertices + (n % number_vertices ));
1115+ distance = getDistance (p ,q );
1116+ if (distance < MagickEpsilon )
1117+ return (INFINITY );
1118+ return ((q -> x - p -> x )* (v -> x - p -> x )+ (v -> y - p -> y )* (q -> y - p -> y ))/sqrt (distance );
11381119}
11391120
1140- static PointInfo rotateVector ( const PointInfo * p ,const double radians )
1121+ static double getFeretDiameter ( PointInfo * p ,PointInfo * q , PointInfo * v )
11411122{
1142- PointInfo
1143- vector ;
1123+ double
1124+ distance ;
11441125
11451126 /*
1146- Rotates the vector p .
1127+ Distance from a point (x,y) to a line passing through p and q .
11471128 */
1148- vector .x = p -> x * cos (radians )- p -> y * sin (radians );
1149- vector .y = p -> x * sin (radians )+ p -> y * cos (radians );
1150- return (vector );
1129+ distance = getDistance (p ,q );
1130+ if (distance < MagickEpsilon )
1131+ return (INFINITY );
1132+ return ((q -> x - p -> x )* (v -> y - p -> y )- (v -> x - p -> x )* (q -> y - p -> y ))/sqrt (distance );
11511133}
11521134
11531135MagickExport PointInfo * GetImageMinimumBoundingBox (Image * image ,
11541136 size_t * number_vertices ,ExceptionInfo * exception )
11551137{
1138+ CaliperInfo
1139+ caliper_info ;
1140+
11561141 double
1157- caliper_area = 0.0 ,
1158- caliper_height = 0.0 ,
1159- caliper_radians = 0.0 ,
1160- caliper_width = 0.0 ,
1161- radians = 0.0 ;
1142+ angle ,
1143+ diameter ;
11621144
11631145 PointInfo
11641146 * bounding_box ,
1165- caliper [4 ] = { { 1.0 , 0.0 }, {-1.0 , 0.0 }, { 0.0 ,-1.0 }, { 0.0 , 1.0 } },
1166- support [4 ][2 ] = { { { 0.0 , 0.0 }, { 0.0 , 0.0 } },
1167- { { 0.0 , 0.0 }, { 0.0 , 0.0 } },
1168- { { 0.0 , 0.0 }, { 0.0 , 0.0 } },
1169- { { 0.0 , 0.0 }, { 0.0 , 0.0 } } },
11701147 * vertices ;
11711148
1172- size_t
1173- hull_vertices ;
1149+ register ssize_t
1150+ i ;
11741151
1175- ssize_t
1176- corner [4 ] = { 0 , 0 , 0 , 0 },
1177- n ;
1152+ size_t
1153+ number_hull_vertices ;
11781154
11791155 /*
11801156 Generate the minimum bounding box with the "Rotating Calipers" algorithm.
@@ -1184,7 +1160,7 @@ MagickExport PointInfo *GetImageMinimumBoundingBox(Image *image,
11841160 if (image -> debug != MagickFalse )
11851161 (void ) LogMagickEvent (TraceEvent ,GetMagickModule (),"%s" ,image -> filename );
11861162 * number_vertices = 0 ;
1187- vertices = GetImageConvexHull (image ,& hull_vertices ,exception );
1163+ vertices = GetImageConvexHull (image ,& number_hull_vertices ,exception );
11881164 if (vertices == (PointInfo * ) NULL )
11891165 return ((PointInfo * ) NULL );
11901166 * number_vertices = 4 ;
@@ -1195,135 +1171,97 @@ MagickExport PointInfo *GetImageMinimumBoundingBox(Image *image,
11951171 vertices = (PointInfo * ) RelinquishMagickMemory (vertices );
11961172 return ((PointInfo * ) NULL );
11971173 }
1198- for (n = 1 ; n < (ssize_t ) hull_vertices ; n ++ )
1199- {
1200- if (vertices [n ].y < vertices [corner [0 ]].y )
1201- corner [0 ]= n ;
1202- if (vertices [n ].y > vertices [corner [1 ]].y )
1203- corner [1 ]= n ;
1204- if (vertices [n ].x < vertices [corner [2 ]].x )
1205- corner [2 ]= n ;
1206- if (vertices [n ].x > vertices [corner [3 ]].x )
1207- corner [3 ]= n ;
1208- }
1209- while (radians < MagickPI )
1174+ caliper_info .area = 2.0 * image -> columns * image -> rows ;
1175+ caliper_info .width = (double ) image -> columns + image -> rows ;
1176+ caliper_info .height = 0.0 ;
1177+ caliper_info .projection = 0.0 ;
1178+ caliper_info .p = (-1 );
1179+ caliper_info .q = (-1 );
1180+ caliper_info .v = (-1 );
1181+ for (i = 0 ; i < (ssize_t ) number_hull_vertices ; i ++ )
12101182 {
12111183 double
1212- angle [4 ],
1213- area ,
1214- height ,
1215- min_angle ,
1216- width ;
1217-
1218- PointInfo
1219- edge [4 ];
1220-
1221- edge [0 ].x = getVertex (vertices ,corner [0 ]+ 1 ,hull_vertices )-> x -
1222- getVertex (vertices ,corner [0 ],hull_vertices )-> x ;
1223- edge [0 ].y = getVertex (vertices ,corner [0 ]+ 1 ,hull_vertices )-> y -
1224- getVertex (vertices ,corner [0 ],hull_vertices )-> y ;
1225- edge [1 ].x = getVertex (vertices ,corner [1 ]+ 1 ,hull_vertices )-> x -
1226- getVertex (vertices ,corner [1 ],hull_vertices )-> x ;
1227- edge [1 ].y = getVertex (vertices ,corner [1 ]+ 1 ,hull_vertices )-> y -
1228- getVertex (vertices ,corner [1 ],hull_vertices )-> y ;
1229- edge [2 ].x = getVertex (vertices ,corner [2 ]+ 1 ,hull_vertices )-> x -
1230- getVertex (vertices ,corner [2 ],hull_vertices )-> x ;
1231- edge [2 ].y = getVertex (vertices ,corner [2 ]+ 1 ,hull_vertices )-> y -
1232- getVertex (vertices ,corner [2 ],hull_vertices )-> y ;
1233- edge [3 ].x = getVertex (vertices ,corner [3 ]+ 1 ,hull_vertices )-> x -
1234- getVertex (vertices ,corner [3 ],hull_vertices )-> x ;
1235- edge [3 ].y = getVertex (vertices ,corner [3 ]+ 1 ,hull_vertices )-> y -
1236- getVertex (vertices ,corner [3 ],hull_vertices )-> y ;
1237- angle [0 ]= getRadians (& edge [0 ],& caliper [0 ]);
1238- angle [1 ]= getRadians (& edge [1 ],& caliper [1 ]);
1239- angle [2 ]= getRadians (& edge [2 ],& caliper [2 ]);
1240- angle [3 ]= getRadians (& edge [3 ],& caliper [3 ]);
1241- if ((IsNaN (angle [0 ]) != MagickFalse ) ||
1242- (IsNaN (angle [1 ]) != MagickFalse ) ||
1243- (IsNaN (angle [2 ]) != MagickFalse ) ||
1244- (IsNaN (angle [3 ]) != MagickFalse ))
1245- break ;
1246- area = 0.0 ;
1247- min_angle = MagickMin (MagickMin (MagickMin (angle [0 ],angle [1 ]),angle [2 ]),
1248- angle [3 ]);
1249- caliper [0 ]= rotateVector (& caliper [0 ],min_angle );
1250- caliper [1 ]= rotateVector (& caliper [1 ],min_angle );
1251- caliper [2 ]= rotateVector (& caliper [2 ],min_angle );
1252- caliper [3 ]= rotateVector (& caliper [3 ],min_angle );
1253- if (fabs (angle [0 ]- min_angle ) < MagickEpsilon )
1254- {
1255- width = getDistance (getVertex (vertices ,corner [1 ],hull_vertices ),
1256- getVertex (vertices ,corner [0 ],hull_vertices ),& caliper [0 ]);
1257- height = getDistance (getVertex (vertices ,corner [3 ],hull_vertices ),
1258- getVertex (vertices ,corner [2 ],hull_vertices ),& caliper [2 ]);
1259- }
1260- else
1261- if (fabs (angle [1 ]- min_angle ) < MagickEpsilon )
1184+ area = 0.0 ,
1185+ max_projection = 0.0 ,
1186+ min_diameter = -1.0 ,
1187+ min_projection = 0.0 ;
1188+
1189+ register ssize_t
1190+ j ,
1191+ k ;
1192+
1193+ ssize_t
1194+ p = -1 ,
1195+ q = -1 ,
1196+ v = -1 ;
1197+
1198+ for (j = 0 ; j < (ssize_t ) number_hull_vertices ; j ++ )
1199+ {
1200+ double
1201+ diameter ;
1202+
1203+ diameter = fabs (getFeretDiameter (& vertices [i ],
1204+ & vertices [(i + 1 ) % number_hull_vertices ],& vertices [j ]));
1205+ if (min_diameter < diameter )
12621206 {
1263- width = getDistance (getVertex (vertices ,corner [0 ],hull_vertices ),
1264- getVertex (vertices ,corner [1 ],hull_vertices ),& caliper [1 ]);
1265- height = getDistance (getVertex (vertices ,corner [3 ],hull_vertices ),
1266- getVertex (vertices ,corner [2 ],hull_vertices ),& caliper [2 ]);
1267- }
1268- else
1269- if (fabs (angle [2 ]- min_angle ) < MagickEpsilon )
1270- {
1271- width = getDistance (getVertex (vertices ,corner [1 ],hull_vertices ),
1272- getVertex (vertices ,corner [0 ],hull_vertices ),& caliper [0 ]);
1273- height = getDistance (getVertex (vertices ,corner [3 ],hull_vertices ),
1274- getVertex (vertices ,corner [2 ],hull_vertices ),& caliper [2 ]);
1275- }
1276- else
1277- {
1278- width = getDistance (getVertex (vertices ,corner [1 ],hull_vertices ),
1279- getVertex (vertices ,corner [0 ],hull_vertices ),& caliper [0 ]);
1280- height = getDistance (getVertex (vertices ,corner [2 ],hull_vertices ),
1281- getVertex (vertices ,corner [3 ],hull_vertices ),& caliper [3 ]);
1282- }
1283- radians += min_angle ;
1284- area = width * height ;
1285- if ((fabs (caliper_area ) < MagickEpsilon ) || (area <= caliper_area ))
1207+ min_diameter = diameter ;
1208+ p = i ;
1209+ q = (i + 1 ) % number_hull_vertices ;
1210+ v = j ;
1211+ }
1212+ }
1213+ for (k = 0 ; k < (ssize_t ) number_hull_vertices ; k ++ )
1214+ {
1215+ double
1216+ projection ;
1217+
1218+ /*
1219+ Rotating calipers.
1220+ */
1221+ projection = getProjection (& vertices [p ],& vertices [q ],& vertices [k ]);
1222+ min_projection = MagickMin (min_projection ,projection );
1223+ max_projection = MagickMax (max_projection ,projection );
1224+ }
1225+ area = min_diameter * (max_projection - min_projection );
1226+ if (caliper_info .area > area )
12861227 {
1287- support [0 ][0 ]= (* getVertex (vertices ,corner [0 ],hull_vertices ));
1288- support [0 ][1 ]= caliper [0 ];
1289- support [1 ][0 ]= (* getVertex (vertices ,corner [1 ],hull_vertices ));
1290- support [1 ][1 ]= caliper [1 ];
1291- support [2 ][0 ]= (* getVertex (vertices ,corner [2 ],hull_vertices ));
1292- support [2 ][1 ]= caliper [2 ];
1293- support [3 ][0 ]= (* getVertex (vertices ,corner [3 ],hull_vertices ));
1294- support [3 ][1 ]= caliper [3 ];
1295- caliper_radians = radians ;
1296- caliper_area = area ;
1297- caliper_width = width ;
1298- caliper_height = height ;
1299- }
1300- if (fabs (angle [0 ]- min_angle ) < MagickEpsilon )
1301- corner [0 ]++ ;
1302- else
1303- if (fabs (angle [1 ]- min_angle ) < MagickEpsilon )
1304- corner [1 ]++ ;
1305- else
1306- if (fabs (angle [2 ]- min_angle ) < MagickEpsilon )
1307- corner [2 ]++ ;
1308- else
1309- corner [3 ]++ ;
1228+ caliper_info .area = area ;
1229+ caliper_info .width = min_diameter ;
1230+ caliper_info .height = max_projection - min_projection ;
1231+ caliper_info .projection = max_projection ;
1232+ caliper_info .p = p ;
1233+ caliper_info .q = q ;
1234+ caliper_info .v = v ;
1235+ }
13101236 }
1311- bounding_box [0 ]= getIntersection (& support [0 ][0 ],& support [0 ][1 ],& support [3 ][0 ],
1312- & support [3 ][1 ]);
1313- bounding_box [1 ]= getIntersection (& support [3 ][0 ],& support [3 ][1 ],& support [1 ][0 ],
1314- & support [1 ][1 ]);
1315- bounding_box [2 ]= getIntersection (& support [1 ][0 ],& support [1 ][1 ],& support [2 ][0 ],
1316- & support [2 ][1 ]);
1317- bounding_box [3 ]= getIntersection (& support [2 ][0 ],& support [2 ][1 ],& support [0 ][0 ],
1318- & support [0 ][1 ]);
1237+ /*
1238+ Initialize minimum bounding box.
1239+ */
1240+ diameter = getFeretDiameter (& vertices [caliper_info .p ],
1241+ & vertices [caliper_info .q ],& vertices [caliper_info .v ]);
1242+ angle = atan2 (vertices [caliper_info .q ].y - vertices [caliper_info .p ].y ,
1243+ vertices [caliper_info .q ].x - vertices [caliper_info .p ].x );
1244+ bounding_box [0 ].x = vertices [caliper_info .p ].x + cos (angle )*
1245+ caliper_info .projection ;
1246+ bounding_box [0 ].y = vertices [caliper_info .p ].y + sin (angle )*
1247+ caliper_info .projection ;
1248+ bounding_box [1 ].x = bounding_box [0 ].x + cos (angle + MagickPI /2.0 )* diameter ;
1249+ bounding_box [1 ].y = bounding_box [0 ].y + sin (angle + MagickPI /2.0 )* diameter ;
1250+ bounding_box [2 ].x = bounding_box [1 ].x + cos (angle )* (- caliper_info .height );
1251+ bounding_box [2 ].y = bounding_box [1 ].y + sin (angle )* (- caliper_info .height );
1252+ bounding_box [3 ].x = bounding_box [2 ].x + cos (angle + MagickPI /2.0 )* (- diameter );
1253+ bounding_box [3 ].y = bounding_box [2 ].y + sin (angle + MagickPI /2.0 )* (- diameter );
1254+ /*
1255+ Export minimum bounding box properties.
1256+ */
13191257 (void ) FormatImageProperty (image ,"minimum-bounding-box:area" ,"%.*g" ,
1320- GetMagickPrecision (),caliper_area );
1258+ GetMagickPrecision (),caliper_info . area );
13211259 (void ) FormatImageProperty (image ,"minimum-bounding-box:width" ,"%.*g" ,
1322- GetMagickPrecision (),caliper_width );
1260+ GetMagickPrecision (),caliper_info . width );
13231261 (void ) FormatImageProperty (image ,"minimum-bounding-box:height" ,"%.*g" ,
1324- GetMagickPrecision (),caliper_height );
1262+ GetMagickPrecision (),caliper_info . height );
13251263 (void ) FormatImageProperty (image ,"minimum-bounding-box:angle" ,"%.*g" ,
1326- GetMagickPrecision (),90.0 - RadiansToDegrees (caliper_radians ));
1264+ GetMagickPrecision (),RadiansToDegrees (angle ));
13271265 vertices = (PointInfo * ) RelinquishMagickMemory (vertices );
13281266 return (bounding_box );
13291267}
0 commit comments