Add support for returning the convex hull of an image

This commit is contained in:
Cristy
2020-03-14 17:56:39 -04:00
parent 1884092b26
commit 73da9ae83c
5 changed files with 373 additions and 16 deletions
+5 -1
View File
@@ -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).
+318
View File
@@ -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 %
% %
% %
+4 -1
View File
@@ -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 *),
+1
View File
@@ -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
View File
@@ -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)