mirror of
https://github.com/ImageMagick/ImageMagick.git
synced 2026-05-31 11:18:42 +02:00
Add support for returning the convex hull of an image
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
2020-03-07 7.0.10-1 Cristy <quetzlzacatenango@image...>
|
||||
2020-03-14 7.0.10-1 Cristy <quetzlzacatenango@image...>
|
||||
* Release ImageMagick version 7.0.10-1, GIT revision 17...
|
||||
|
||||
2020-03-14 7.0.10-1 Cristy <quetzlzacatenango@image...>
|
||||
* Add support for returning the convex hull of an image with the
|
||||
%[canvas-hull] property.
|
||||
|
||||
2020-03-09 7.0.10-1 Dirk Lemstra <dirk@lem.....org>
|
||||
* Added option to specify the preferred version when writing a PDF file with
|
||||
-define pdf:version=version (e.g. 1.7).
|
||||
|
||||
@@ -519,6 +519,324 @@ MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
|
||||
% %
|
||||
% %
|
||||
% %
|
||||
+ G e t I m a g e C o n v e x H u l l %
|
||||
% %
|
||||
% %
|
||||
% %
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%
|
||||
% GetImageConvexHull() returns the convex hull points of an image canvas.
|
||||
%
|
||||
% The format of the GetImageConvexHull method is:
|
||||
%
|
||||
% PointInfo *GetImageConvexHull(const Image *image,
|
||||
% size_t number_coordinates,ExceptionInfo *exception)
|
||||
%
|
||||
% A description of each parameter follows:
|
||||
%
|
||||
% o image: the image.
|
||||
%
|
||||
% o number_coordinates: the number of coordinates in the convex hull.
|
||||
%
|
||||
% o exception: return any errors or warnings in this structure.
|
||||
%
|
||||
*/
|
||||
|
||||
static double LexicographicalSort(PointInfo *p1,PointInfo *p2,PointInfo *p3)
|
||||
{
|
||||
/*
|
||||
Sort first by x-coordinate, and in case of a tie, by y-coordinate.
|
||||
*/
|
||||
return((p2->x-p1->x)*(p3->y-p1->y)-(p2->y-p1->y)*(p3->x-p1->x));
|
||||
}
|
||||
|
||||
void ConvexHull(PointInfo *coordinates,size_t number_coordinates,
|
||||
PointInfo ***monotone_chain,size_t *chain_length)
|
||||
{
|
||||
PointInfo
|
||||
**chain;
|
||||
|
||||
register ssize_t
|
||||
i;
|
||||
|
||||
size_t
|
||||
demark,
|
||||
n;
|
||||
|
||||
/*
|
||||
Construct the upper and lower hulls: rightmost to leftmost counterclockwise.
|
||||
*/
|
||||
chain=(*monotone_chain);
|
||||
n=0;
|
||||
for (i=0; i < (ssize_t) number_coordinates; i++)
|
||||
{
|
||||
while ((n >= 2) &&
|
||||
(LexicographicalSort(chain[n-2],chain[n-1],&coordinates[i]) <= 0.0))
|
||||
n--;
|
||||
chain[n++]=(&coordinates[i]);
|
||||
}
|
||||
demark=n+1;
|
||||
for (i=(ssize_t) number_coordinates-2; i >= 0; i--)
|
||||
{
|
||||
while ((n >= demark) &&
|
||||
(LexicographicalSort(chain[n-2],chain[n-1],&coordinates[i]) <= 0.0))
|
||||
n--;
|
||||
chain[n++]=(&coordinates[i]);
|
||||
}
|
||||
*monotone_chain=chain;
|
||||
*chain_length=n;
|
||||
}
|
||||
|
||||
static PixelInfo GetEdgeBackgroundColor(const Image *image,
|
||||
const CacheView *image_view,ExceptionInfo *exception)
|
||||
{
|
||||
CacheView
|
||||
*edge_view;
|
||||
|
||||
const char
|
||||
*artifact;
|
||||
|
||||
double
|
||||
edge_factor,
|
||||
factor;
|
||||
|
||||
Image
|
||||
*edge_image;
|
||||
|
||||
PixelInfo
|
||||
background,
|
||||
edge_background,
|
||||
pixel;
|
||||
|
||||
RectangleInfo
|
||||
edge_geometry;
|
||||
|
||||
register ssize_t
|
||||
i;
|
||||
|
||||
register const Quantum
|
||||
*p;
|
||||
|
||||
ssize_t
|
||||
y;
|
||||
|
||||
/*
|
||||
Identify background color from edge of image.
|
||||
*/
|
||||
edge_factor=(-1.0);
|
||||
GetPixelInfo(image,&edge_background);
|
||||
for (i=0; i < 4; i++)
|
||||
{
|
||||
GravityType
|
||||
gravity;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
{
|
||||
gravity=WestGravity;
|
||||
edge_geometry.width=1;
|
||||
edge_geometry.height=0;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
gravity=EastGravity;
|
||||
edge_geometry.width=1;
|
||||
edge_geometry.height=0;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
gravity=NorthGravity;
|
||||
edge_geometry.width=0;
|
||||
edge_geometry.height=1;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
gravity=SouthGravity;
|
||||
edge_geometry.width=0;
|
||||
edge_geometry.height=1;
|
||||
}
|
||||
}
|
||||
edge_geometry.x=0;
|
||||
edge_geometry.y=0;
|
||||
switch (gravity)
|
||||
{
|
||||
case NorthWestGravity:
|
||||
case NorthGravity:
|
||||
default:
|
||||
{
|
||||
p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
|
||||
break;
|
||||
}
|
||||
case NorthEastGravity:
|
||||
case EastGravity:
|
||||
{
|
||||
p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
|
||||
exception);
|
||||
break;
|
||||
}
|
||||
case SouthEastGravity:
|
||||
case SouthGravity:
|
||||
{
|
||||
p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
|
||||
(ssize_t) image->rows-1,1,1,exception);
|
||||
break;
|
||||
}
|
||||
case SouthWestGravity:
|
||||
case WestGravity:
|
||||
{
|
||||
p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
|
||||
exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GetPixelInfoPixel(image,p,&background);
|
||||
artifact=GetImageArtifact(image,"convex-hull:background-color");
|
||||
if (artifact != (const char *) NULL)
|
||||
(void) QueryColorCompliance(artifact,AllCompliance,&background,exception);
|
||||
GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
|
||||
edge_image=CropImage(image,&edge_geometry,exception);
|
||||
if (edge_image == (Image *) NULL)
|
||||
break;
|
||||
factor=0.0;
|
||||
edge_view=AcquireVirtualCacheView(edge_image,exception);
|
||||
for (y=0; y < (ssize_t) edge_image->rows; y++)
|
||||
{
|
||||
register ssize_t
|
||||
x;
|
||||
|
||||
p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,
|
||||
exception);
|
||||
if (p == (const Quantum *) NULL)
|
||||
break;
|
||||
for (x=0; x < (ssize_t) edge_image->columns; x++)
|
||||
{
|
||||
GetPixelInfoPixel(edge_image,p,&pixel);
|
||||
if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
|
||||
factor++;
|
||||
p+=GetPixelChannels(edge_image);
|
||||
}
|
||||
}
|
||||
factor/=((double) edge_image->columns*edge_image->rows);
|
||||
if (factor > edge_factor)
|
||||
{
|
||||
edge_background=background;
|
||||
edge_factor=factor;
|
||||
}
|
||||
edge_view=DestroyCacheView(edge_view);
|
||||
edge_image=DestroyImage(edge_image);
|
||||
}
|
||||
return(edge_background);
|
||||
}
|
||||
|
||||
MagickExport PointInfo *GetImageConvexHull(const Image *image,
|
||||
size_t *number_coordinates,ExceptionInfo *exception)
|
||||
{
|
||||
CacheView
|
||||
*image_view;
|
||||
|
||||
MagickBooleanType
|
||||
status;
|
||||
|
||||
MemoryInfo
|
||||
*coordinate_info;
|
||||
|
||||
PixelInfo
|
||||
background;
|
||||
|
||||
PointInfo
|
||||
*convex_hull,
|
||||
*coordinates,
|
||||
**monotone_chain;
|
||||
|
||||
size_t
|
||||
n;
|
||||
|
||||
ssize_t
|
||||
y;
|
||||
|
||||
/*
|
||||
Identify convex hull coordinates of image foreground object(s).
|
||||
*/
|
||||
assert(image != (Image *) NULL);
|
||||
assert(image->signature == MagickCoreSignature);
|
||||
if (image->debug != MagickFalse)
|
||||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
||||
*number_coordinates=0;
|
||||
coordinate_info=AcquireVirtualMemory(image->columns,image->rows*
|
||||
sizeof(*coordinates));
|
||||
monotone_chain=(PointInfo **) AcquireQuantumMemory(2*image->columns,2*
|
||||
image->rows*sizeof(*monotone_chain));
|
||||
if ((coordinate_info == (MemoryInfo *) NULL) ||
|
||||
(monotone_chain == (PointInfo **) NULL))
|
||||
{
|
||||
if (monotone_chain != (PointInfo **) NULL)
|
||||
monotone_chain=(PointInfo **) RelinquishMagickMemory(monotone_chain);
|
||||
if (coordinate_info != (MemoryInfo *) NULL)
|
||||
coordinate_info=RelinquishVirtualMemory(coordinate_info);
|
||||
return((PointInfo *) NULL);
|
||||
}
|
||||
coordinates=(PointInfo *) GetVirtualMemoryBlob(coordinate_info);
|
||||
image_view=AcquireVirtualCacheView(image,exception);
|
||||
background=GetEdgeBackgroundColor(image,image_view,exception);
|
||||
status=MagickTrue;
|
||||
n=0;
|
||||
for (y=0; y < (ssize_t) image->rows; y++)
|
||||
{
|
||||
register const Quantum
|
||||
*p;
|
||||
|
||||
register ssize_t
|
||||
x;
|
||||
|
||||
if (status == MagickFalse)
|
||||
continue;
|
||||
p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
|
||||
if (p == (const Quantum *) NULL)
|
||||
{
|
||||
status=MagickFalse;
|
||||
continue;
|
||||
}
|
||||
for (x=0; x < (ssize_t) image->columns; x++)
|
||||
{
|
||||
PixelInfo
|
||||
pixel;
|
||||
|
||||
GetPixelInfoPixel(image,p,&pixel);
|
||||
if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
|
||||
{
|
||||
coordinates[n].x=(double) x;
|
||||
coordinates[n].y=(double) y;
|
||||
n++;
|
||||
}
|
||||
p+=GetPixelChannels(image);
|
||||
}
|
||||
}
|
||||
image_view=DestroyCacheView(image_view);
|
||||
/*
|
||||
Return the convex hull of the image foreground object(s).
|
||||
*/
|
||||
ConvexHull(coordinates,n,&monotone_chain,number_coordinates);
|
||||
convex_hull=(PointInfo *) AcquireQuantumMemory(*number_coordinates,
|
||||
sizeof(*convex_hull));
|
||||
if (convex_hull == (PointInfo *) NULL)
|
||||
{
|
||||
coordinate_info=RelinquishVirtualMemory(coordinate_info);
|
||||
return((PointInfo *) NULL);
|
||||
}
|
||||
for (n=0; n < *number_coordinates; n++)
|
||||
convex_hull[n]=(*monotone_chain[n]);
|
||||
monotone_chain=(PointInfo **) RelinquishMagickMemory(monotone_chain);
|
||||
coordinate_info=RelinquishVirtualMemory(coordinate_info);
|
||||
return(convex_hull);
|
||||
}
|
||||
|
||||
/*
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% %
|
||||
% %
|
||||
% %
|
||||
% G e t I m a g e D e p t h %
|
||||
% %
|
||||
% %
|
||||
|
||||
@@ -38,8 +38,11 @@ extern MagickExport MagickBooleanType
|
||||
SetImageDepth(Image *,const size_t,ExceptionInfo *),
|
||||
SetImageType(Image *,const ImageType,ExceptionInfo *);
|
||||
|
||||
extern MagickExport PointInfo
|
||||
*GetImageConvexHull(const Image *,size_t *,ExceptionInfo *);
|
||||
|
||||
extern MagickExport RectangleInfo
|
||||
GetImageBoundingBox(const Image *,ExceptionInfo *exception);
|
||||
GetImageBoundingBox(const Image *,ExceptionInfo *);
|
||||
|
||||
extern MagickExport size_t
|
||||
GetImageDepth(const Image *,ExceptionInfo *),
|
||||
|
||||
@@ -2718,6 +2718,7 @@ MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
|
||||
sigma+=0.25;
|
||||
if (preview_image == (Image *) NULL)
|
||||
break;
|
||||
preview_image->alpha_trait=UndefinedPixelTrait;
|
||||
(void) DeleteImageProperty(preview_image,"label");
|
||||
(void) SetImageProperty(preview_image,"label",label,exception);
|
||||
AppendImageToList(&images,preview_image);
|
||||
|
||||
+45
-14
@@ -2405,7 +2405,7 @@ MagickExport const char *GetImageProperty(const Image *image,
|
||||
%
|
||||
% The returned string is stored in a structure somewhere, and should not be
|
||||
% directly freed. If the string was generated (common) the string will be
|
||||
% stored as as either as artifact or option 'get-property'. These may be
|
||||
% stored as as either as artifact or option 'magick-property'. These may be
|
||||
% deleted (cleaned up) when no longer required, but neither artifact or
|
||||
% option is guranteed to exist.
|
||||
%
|
||||
@@ -2814,13 +2814,13 @@ static const char *GetMagickPropertyLetter(ImageInfo *image_info,
|
||||
*/
|
||||
if (image != (Image *) NULL)
|
||||
{
|
||||
(void) SetImageArtifact(image,"get-property",value);
|
||||
return(GetImageArtifact(image,"get-property"));
|
||||
(void) SetImageArtifact(image,"magick-property",value);
|
||||
return(GetImageArtifact(image,"magick-property"));
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) SetImageOption(image_info,"get-property",value);
|
||||
return(GetImageOption(image_info,"get-property"));
|
||||
(void) SetImageOption(image_info,"magick-property",value);
|
||||
return(GetImageOption(image_info,"magick-property"));
|
||||
}
|
||||
}
|
||||
return((char *) NULL);
|
||||
@@ -2873,7 +2873,7 @@ MagickExport const char *GetMagickProperty(ImageInfo *image_info,
|
||||
|
||||
WarnNoImageReturn("\"%%[%s]\"",property);
|
||||
geometry=GetImageBoundingBox(image,exception);
|
||||
(void) FormatLocaleString(value,MagickPathExtent,"%g,%g %g,%g\n",
|
||||
(void) FormatLocaleString(value,MagickPathExtent,"%g,%g %g,%g",
|
||||
(double) geometry.x,(double) geometry.y,
|
||||
(double) geometry.x+geometry.width,
|
||||
(double) geometry.y+geometry.height);
|
||||
@@ -2924,6 +2924,37 @@ MagickExport const char *GetMagickProperty(ImageInfo *image_info,
|
||||
image->compression);
|
||||
break;
|
||||
}
|
||||
if (LocaleCompare("convex-hull",property) == 0)
|
||||
{
|
||||
char
|
||||
*points;
|
||||
|
||||
PointInfo
|
||||
*convex_hull;
|
||||
|
||||
register ssize_t
|
||||
n;
|
||||
|
||||
size_t
|
||||
number_points;
|
||||
|
||||
WarnNoImageReturn("\"%%[%s]\"",property);
|
||||
convex_hull=GetImageConvexHull(image,&number_points,exception);
|
||||
if (convex_hull == (PointInfo *) NULL)
|
||||
break;
|
||||
points=AcquireString("");
|
||||
for (n=0; n < (ssize_t) number_points; n++)
|
||||
{
|
||||
(void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
|
||||
convex_hull[n].x,convex_hull[n].y);
|
||||
(void) ConcatenateString(&points,value);
|
||||
}
|
||||
convex_hull=(PointInfo *) RelinquishMagickMemory(convex_hull);
|
||||
(void) SetImageArtifact(image,"convex-hull",points);
|
||||
points=DestroyString(points);
|
||||
string=GetImageArtifact(image,"convex-hull");
|
||||
break;
|
||||
}
|
||||
if (LocaleCompare("copyright",property) == 0)
|
||||
{
|
||||
(void) CopyMagickString(value,GetMagickCopyright(),MagickPathExtent);
|
||||
@@ -3323,13 +3354,13 @@ MagickExport const char *GetMagickProperty(ImageInfo *image_info,
|
||||
*/
|
||||
if (image != (Image *) NULL)
|
||||
{
|
||||
(void) SetImageArtifact(image,"get-property",value);
|
||||
return(GetImageArtifact(image,"get-property"));
|
||||
(void) SetImageArtifact(image,"magick-property",value);
|
||||
return(GetImageArtifact(image,"magick-property"));
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) SetImageOption(image_info,"get-property",value);
|
||||
return(GetImageOption(image_info,"get-property"));
|
||||
(void) SetImageOption(image_info,"magick-property",value);
|
||||
return(GetImageOption(image_info,"magick-property"));
|
||||
}
|
||||
}
|
||||
return((char *) NULL);
|
||||
@@ -3676,8 +3707,8 @@ RestoreMSCWarning
|
||||
if (string != (char *) NULL)
|
||||
{
|
||||
AppendString2Text(string);
|
||||
(void) DeleteImageArtifact(property_image,"get-property");
|
||||
(void) DeleteImageOption(property_info,"get-property");
|
||||
(void) DeleteImageArtifact(property_image,"magick-property");
|
||||
(void) DeleteImageOption(property_info,"magick-property");
|
||||
continue;
|
||||
}
|
||||
(void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
|
||||
@@ -3963,8 +3994,8 @@ RestoreMSCWarning
|
||||
if (string != (const char *) NULL)
|
||||
{
|
||||
AppendString2Text(string);
|
||||
(void)DeleteImageArtifact(property_image,"get-property");
|
||||
(void)DeleteImageOption(property_info,"get-property");
|
||||
(void) DeleteImageArtifact(property_image,"magick-property");
|
||||
(void) DeleteImageOption(property_info,"magick-property");
|
||||
continue;
|
||||
}
|
||||
if (IsGlob(pattern) != MagickFalse)
|
||||
|
||||
Reference in New Issue
Block a user