1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % JJJJJ SSSSS OOO N N % 7 % J SS O O NN N % 8 % J SSS O O N N N % 9 % J J SS O O N NN % 10 % JJJ SSSSS OOO N N % 11 % % 12 % % 13 % Write Info About the Image in JSON Format. % 14 % % 15 % Software Design % 16 % Cristy % 17 % January 2014 % 18 % % 19 % % 20 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21 % dedicated to making software imaging solutions freely available. % 22 % % 23 % You may not use this file except in compliance with the License. You may % 24 % obtain a copy of the License at % 25 % % 26 % http://www.imagemagick.org/script/license.php % 27 % % 28 % Unless required by applicable law or agreed to in writing, software % 29 % distributed under the License is distributed on an "AS IS" BASIS, % 30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31 % See the License for the specific language governing permissions and % 32 % limitations under the License. % 33 % % 34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 % 36 % 37 */ 38 39 /* 41 Include declarations. 42 */ 43 #include "MagickCore/studio.h" 44 #include "MagickCore/artifact.h" 45 #include "MagickCore/attribute.h" 46 #include "MagickCore/blob.h" 47 #include "MagickCore/blob-private.h" 48 #include "MagickCore/cache.h" 49 #include "MagickCore/colorspace.h" 50 #include "MagickCore/colorspace-private.h" 51 #include "MagickCore/constitute.h" 52 #include "MagickCore/exception.h" 53 #include "MagickCore/exception-private.h" 54 #include "MagickCore/feature.h" 55 #include "MagickCore/image.h" 56 #include "MagickCore/image-private.h" 57 #include "MagickCore/list.h" 58 #include "MagickCore/magick.h" 59 #include "MagickCore/memory_.h" 60 #include "MagickCore/monitor.h" 61 #include "MagickCore/monitor-private.h" 62 #include "MagickCore/option.h" 63 #include "MagickCore/pixel.h" 64 #include "MagickCore/pixel-accessor.h" 65 #include "MagickCore/pixel-private.h" 66 #include "MagickCore/prepress.h" 67 #include "MagickCore/property.h" 68 #include "MagickCore/quantum-private.h" 69 #include "MagickCore/registry.h" 70 #include "MagickCore/signature.h" 71 #include "MagickCore/static.h" 72 #include "MagickCore/statistic.h" 73 #include "MagickCore/string_.h" 74 #include "MagickCore/string-private.h" 75 #include "MagickCore/utility.h" 76 #include "MagickCore/version.h" 77 #include "MagickCore/module.h" 78 79 /* 81 Forward declarations. 82 */ 83 static MagickBooleanType 84 WriteJSONImage(const ImageInfo *,Image *,ExceptionInfo *); 85 86 /* 88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 89 % % 90 % % 91 % % 92 % R e g i s t e r J S O N I m a g e % 93 % % 94 % % 95 % % 96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 97 % 98 % RegisterJSONImage() adds attributes for the JSON image format to 99 % the list of supported formats. The attributes include the image format 100 % tag, a method to read and/or write the format, whether the format 101 % supports the saving of more than one frame to the same file or blob, 102 % whether the format supports native in-memory I/O, and a brief 103 % description of the format. 104 % 105 % The format of the RegisterJSONImage method is: 106 % 107 % size_t RegisterJSONImage(void) 108 % 109 */ 110 ModuleExport size_t RegisterJSONImage(void) 111 { 112 MagickInfo 113 *entry; 114 115 entry=AcquireMagickInfo("JSON","JSON","The image format and characteristics"); 116 entry->encoder=(EncodeImageHandler *) WriteJSONImage; 117 entry->mime_type=ConstantString("application/json"); 118 entry->flags^=CoderBlobSupportFlag; 119 (void) RegisterMagickInfo(entry); 120 return(MagickImageCoderSignature); 121 } 122 123 /* 125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 126 % % 127 % % 128 % % 129 % U n r e g i s t e r J S O N I m a g e % 130 % % 131 % % 132 % % 133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 134 % 135 % UnregisterJSONImage() removes format registrations made by the 136 % JSON module from the list of supported formats. 137 % 138 % The format of the UnregisterJSONImage method is: 139 % 140 % UnregisterJSONImage(void) 141 % 142 */ 143 ModuleExport void UnregisterJSONImage(void) 144 { 145 (void) UnregisterMagickInfo("JSON"); 146 } 147 148 /* 150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 151 % % 152 % % 153 % % 154 % W r i t e J S O N I m a g e % 155 % % 156 % % 157 % % 158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 159 % 160 % WriteJSONImage writes the image attributes in the JSON format. 161 % 162 % The format of the WriteJSONImage method is: 163 % 164 % MagickBooleanType WriteJSONImage(const ImageInfo *image_info, 165 % Image *image,ExceptionInfo *exception) 166 % 167 % A description of each parameter follows. 168 % 169 % o image_info: the image info. 170 % 171 % o image: The image. 172 % 173 % o exception: return any errors or warnings in this structure. 174 % 175 */ 176 177 static void JsonFormatLocaleFile(FILE *file,const char *format,const char *value) 178 { 179 char 180 *escaped_json; 181 182 register char 183 *q; 184 185 register const char 186 *p; 187 188 size_t 189 length; 190 191 assert(format != (const char *) NULL); 192 if (value == (char *) NULL || *value == '\0') 193 { 194 (void) FormatLocaleFile(file,format,"null"); 195 return; 196 } 197 length=strlen(value)+2; 198 /* 199 Find all the chars that need escaping and increase the dest length counter 200 */ 201 for (p=value; *p != '\0'; p++) 202 { 203 switch (*p) 204 { 205 case '"': 206 case '\b': 207 case '\f': 208 case '\n': 209 case '\r': 210 case '\t': 211 case '\\': 212 if (~length < 1) 213 return; 214 length++; 215 break; 216 default: 217 break; 218 } 219 } 220 escaped_json=(char *) NULL; 221 if (~length >= (MagickPathExtent-1)) 222 escaped_json=(char *) AcquireQuantumMemory(length+MagickPathExtent, 223 sizeof(*escaped_json)); 224 if (escaped_json == (char *) NULL) 225 { 226 (void) FormatLocaleFile(file,format,"null"); 227 return; 228 } 229 q=escaped_json; 230 *q++='"'; 231 for (p=value; *p != '\0'; p++) 232 { 233 switch (*p) 234 { 235 case '"': 236 *q++='\\'; 237 *q++=(*p); 238 break; 239 case '\b': 240 *q++='\\'; 241 *q++='b'; 242 break; 243 case '\f': 244 *q++='\\'; 245 *q++='f'; 246 break; 247 case '\n': 248 *q++='\\'; 249 *q++='n'; 250 break; 251 case '\r': 252 *q++='\\'; 253 *q++='r'; 254 break; 255 case '\t': 256 *q++='\\'; 257 *q++='t'; 258 break; 259 case '\\': 260 *q++='\\'; 261 *q++='\\'; 262 break; 263 default: 264 *q++=(*p); 265 break; 266 } 267 } 268 *q++='"'; 269 *q='\0'; 270 (void) FormatLocaleFile(file,format,escaped_json); 271 (void) DestroyString(escaped_json); 272 } 273 274 static ChannelStatistics *GetLocationStatistics(const Image *image, 275 const StatisticType type,ExceptionInfo *exception) 276 { 277 ChannelStatistics 278 *channel_statistics; 279 280 register ssize_t 281 i; 282 283 ssize_t 284 y; 285 286 assert(image != (Image *) NULL); 287 assert(image->signature == MagickCoreSignature); 288 if (image->debug != MagickFalse) 289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 290 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory( 291 MaxPixelChannels+1,sizeof(*channel_statistics)); 292 if (channel_statistics == (ChannelStatistics *) NULL) 293 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 294 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)* 295 sizeof(*channel_statistics)); 296 for (i=0; i <= (ssize_t) MaxPixelChannels; i++) 297 { 298 switch (type) 299 { 300 case MaximumStatistic: 301 default: 302 { 303 channel_statistics[i].maxima=(-MagickMaximumValue); 304 break; 305 } 306 case MinimumStatistic: 307 { 308 channel_statistics[i].minima=MagickMaximumValue; 309 break; 310 } 311 } 312 } 313 for (y=0; y < (ssize_t) image->rows; y++) 314 { 315 register const Quantum 316 *magick_restrict p; 317 318 register ssize_t 319 x; 320 321 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 322 if (p == (const Quantum *) NULL) 323 break; 324 for (x=0; x < (ssize_t) image->columns; x++) 325 { 326 register ssize_t 327 i; 328 329 if (GetPixelReadMask(image,p) == 0) 330 { 331 p+=GetPixelChannels(image); 332 continue; 333 } 334 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 335 { 336 PixelChannel channel=GetPixelChannelChannel(image,i); 337 PixelTrait traits=GetPixelChannelTraits(image,channel); 338 if (traits == UndefinedPixelTrait) 339 continue; 340 switch (type) 341 { 342 case MaximumStatistic: 343 default: 344 { 345 if ((double) p[i] > channel_statistics[channel].maxima) 346 channel_statistics[channel].maxima=(double) p[i]; 347 break; 348 } 349 case MinimumStatistic: 350 { 351 if ((double) p[i] < channel_statistics[channel].minima) 352 channel_statistics[channel].minima=(double) p[i]; 353 break; 354 } 355 } 356 } 357 p+=GetPixelChannels(image); 358 } 359 } 360 return(channel_statistics); 361 } 362 363 static ssize_t PrintChannelFeatures(FILE *file,const PixelChannel channel, 364 const char *name,const MagickBooleanType separator, 365 const ChannelFeatures *channel_features) 366 { 367 #define PrintFeature(feature) \ 368 GetMagickPrecision(),(feature)[0], \ 369 GetMagickPrecision(),(feature)[1], \ 370 GetMagickPrecision(),(feature)[2], \ 371 GetMagickPrecision(),(feature)[3], \ 372 GetMagickPrecision(),((feature)[0]+(feature)[1]+(feature)[2]+(feature)[3])/4.0 \ 373 374 #define FeaturesFormat " \"%s\": {\n" \ 375 " \"angularSecondMoment\": {\n" \ 376 " \"horizontal\": \"%.*g\",\n" \ 377 " \"vertical\": \"%.*g\",\n" \ 378 " \"leftDiagonal\": \"%.*g\",\n" \ 379 " \"rightDiagonal\": \"%.*g\",\n" \ 380 " \"average\": \"%.*g\"\n" \ 381 " },\n" \ 382 " \"contrast\": {\n" \ 383 " \"horizontal\": \"%.*g\",\n" \ 384 " \"vertical\": \"%.*g\",\n" \ 385 " \"leftDiagonal\": \"%.*g\",\n" \ 386 " \"rightDiagonal\": \"%.*g\",\n" \ 387 " \"average\": \"%.*g\"\n" \ 388 " },\n" \ 389 " \"correlation\": {\n" \ 390 " \"horizontal\": \"%.*g\",\n" \ 391 " \"vertical\": \"%.*g\",\n" \ 392 " \"leftDiagonal\": \"%.*g\",\n" \ 393 " \"rightDiagonal\": \"%.*g\",\n" \ 394 " \"average\": \"%.*g\"\n" \ 395 " },\n" \ 396 " \"sumOfSquaresVariance\": {\n" \ 397 " \"horizontal\": \"%.*g\",\n" \ 398 " \"vertical\": \"%.*g\",\n" \ 399 " \"leftDiagonal\": \"%.*g\",\n" \ 400 " \"rightDiagonal\": \"%.*g\",\n" \ 401 " \"average\": \"%.*g\"\n" \ 402 " },\n" \ 403 " \"inverseDifferenceMoment\": {\n" \ 404 " \"horizontal\": \"%.*g\",\n" \ 405 " \"vertical\": \"%.*g\",\n" \ 406 " \"leftDiagonal\": \"%.*g\",\n" \ 407 " \"rightDiagonal\": \"%.*g\",\n" \ 408 " \"average\": \"%.*g\"\n" \ 409 " },\n" \ 410 " \"sumAverage\": {\n" \ 411 " \"horizontal\": \"%.*g\",\n" \ 412 " \"vertical\": \"%.*g\",\n" \ 413 " \"leftDiagonal\": \"%.*g\",\n" \ 414 " \"rightDiagonal\": \"%.*g\",\n" \ 415 " \"average\": \"%.*g\"\n" \ 416 " },\n" \ 417 " \"sumVariance\": {\n" \ 418 " \"horizontal\": \"%.*g\",\n" \ 419 " \"vertical\": \"%.*g\",\n" \ 420 " \"leftDiagonal\": \"%.*g\",\n" \ 421 " \"rightDiagonal\": \"%.*g\",\n" \ 422 " \"average\": \"%.*g\"\n" \ 423 " },\n" \ 424 " \"sumEntropy\": {\n" \ 425 " \"horizontal\": \"%.*g\",\n" \ 426 " \"vertical\": \"%.*g\",\n" \ 427 " \"leftDiagonal\": \"%.*g\",\n" \ 428 " \"rightDiagonal\": \"%.*g\",\n" \ 429 " \"average\": \"%.*g\"\n" \ 430 " },\n" \ 431 " \"entropy\": {\n" \ 432 " \"horizontal\": \"%.*g\",\n" \ 433 " \"vertical\": \"%.*g\",\n" \ 434 " \"leftDiagonal\": \"%.*g\",\n" \ 435 " \"rightDiagonal\": \"%.*g\",\n" \ 436 " \"average\": \"%.*g\"\n" \ 437 " },\n" \ 438 " \"differenceVariance\": {\n" \ 439 " \"horizontal\": \"%.*g\",\n" \ 440 " \"vertical\": \"%.*g\",\n" \ 441 " \"leftDiagonal\": \"%.*g\",\n" \ 442 " \"rightDiagonal\": \"%.*g\",\n" \ 443 " \"average\": \"%.*g\"\n" \ 444 " },\n" \ 445 " \"differenceEntropy\": {\n" \ 446 " \"horizontal\": \"%.*g\",\n" \ 447 " \"vertical\": \"%.*g\",\n" \ 448 " \"leftDiagonal\": \"%.*g\",\n" \ 449 " \"rightDiagonal\": \"%.*g\",\n" \ 450 " \"average\": \"%.*g\"\n" \ 451 " },\n" \ 452 " \"informationMeasureOfCorrelation1\": {\n" \ 453 " \"horizontal\": \"%.*g\",\n" \ 454 " \"vertical\": \"%.*g\",\n" \ 455 " \"leftDiagonal\": \"%.*g\",\n" \ 456 " \"rightDiagonal\": \"%.*g\",\n" \ 457 " \"average\": \"%.*g\"\n" \ 458 " },\n" \ 459 " \"informationMeasureOfCorrelation2\": {\n" \ 460 " \"horizontal\": \"%.*g\",\n" \ 461 " \"vertical\": \"%.*g\",\n" \ 462 " \"leftDiagonal\": \"%.*g\",\n" \ 463 " \"rightDiagonal\": \"%.*g\",\n" \ 464 " \"average\": \"%.*g\"\n" \ 465 " },\n" \ 466 " \"maximumCorrelationCoefficient\": {\n" \ 467 " \"horizontal\": \"%.*g\",\n" \ 468 " \"vertical\": \"%.*g\",\n" \ 469 " \"leftDiagonal\": \"%.*g\",\n" \ 470 " \"rightDiagonal\": \"%.*g\",\n" \ 471 " \"average\": \"%.*g\"\n" \ 472 " }\n" 473 474 ssize_t 475 n; 476 477 n=FormatLocaleFile(file,FeaturesFormat,name, 478 PrintFeature(channel_features[channel].angular_second_moment), 479 PrintFeature(channel_features[channel].contrast), 480 PrintFeature(channel_features[channel].correlation), 481 PrintFeature(channel_features[channel].variance_sum_of_squares), 482 PrintFeature(channel_features[channel].inverse_difference_moment), 483 PrintFeature(channel_features[channel].sum_average), 484 PrintFeature(channel_features[channel].sum_variance), 485 PrintFeature(channel_features[channel].sum_entropy), 486 PrintFeature(channel_features[channel].entropy), 487 PrintFeature(channel_features[channel].difference_variance), 488 PrintFeature(channel_features[channel].difference_entropy), 489 PrintFeature(channel_features[channel].measure_of_correlation_1), 490 PrintFeature(channel_features[channel].measure_of_correlation_2), 491 PrintFeature(channel_features[channel].maximum_correlation_coefficient)); 492 (void) FormatLocaleFile(file," }"); 493 if (separator != MagickFalse) 494 (void) FormatLocaleFile(file,","); 495 (void) FormatLocaleFile(file,"\n"); 496 return(n); 497 } 498 499 static ssize_t PrintChannelLocations(FILE *file,const Image *image, 500 const PixelChannel channel,const char *name,const StatisticType type, 501 const size_t max_locations,const MagickBooleanType separator, 502 const ChannelStatistics *channel_statistics) 503 { 504 double 505 target; 506 507 ExceptionInfo 508 *exception; 509 510 ssize_t 511 n, 512 y; 513 514 switch (type) 515 { 516 case MaximumStatistic: 517 default: 518 { 519 target=channel_statistics[channel].maxima; 520 break; 521 } 522 case MinimumStatistic: 523 { 524 target=channel_statistics[channel].minima; 525 break; 526 } 527 } 528 (void) FormatLocaleFile(file," \"%s\": {\n \"intensity\": " 529 "\"%.*g\",\n",name,GetMagickPrecision(),QuantumScale*target); 530 exception=AcquireExceptionInfo(); 531 n=0; 532 for (y=0; y < (ssize_t) image->rows; y++) 533 { 534 register const Quantum 535 *p; 536 537 ssize_t 538 offset, 539 x; 540 541 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 542 if (p == (const Quantum *) NULL) 543 break; 544 for (x=0; x < (ssize_t) image->columns; x++) 545 { 546 MagickBooleanType 547 match; 548 549 PixelTrait traits=GetPixelChannelTraits(image,channel); 550 if (traits == UndefinedPixelTrait) 551 continue; 552 offset=GetPixelChannelOffset(image,channel); 553 match=fabs((double) (p[offset]-target)) < 0.5 ? MagickTrue : MagickFalse; 554 if (match != MagickFalse) 555 { 556 if ((max_locations != 0) && (n >= (ssize_t) max_locations)) 557 break; 558 if (n != 0) 559 (void) FormatLocaleFile(file,",\n"); 560 (void) FormatLocaleFile(file," \"location%.20g\": {\n" 561 " \"x\": %.20g,\n \"y\": %.20g\n" 562 " }",(double) n,(double) x,(double) y); 563 n++; 564 } 565 p+=GetPixelChannels(image); 566 } 567 if (x < (ssize_t) image->columns) 568 break; 569 } 570 (void) FormatLocaleFile(file,"\n }"); 571 if (separator != MagickFalse) 572 (void) FormatLocaleFile(file,","); 573 (void) FormatLocaleFile(file,"\n"); 574 return(n); 575 } 576 577 static ssize_t PrintChannelMoments(FILE *file,const PixelChannel channel, 578 const char *name,const MagickBooleanType separator, 579 const ChannelMoments *channel_moments) 580 { 581 register ssize_t 582 i; 583 584 ssize_t 585 n; 586 587 n=FormatLocaleFile(file," \"%s\": {\n",name); 588 n+=FormatLocaleFile(file," \"centroid\": {\n " 589 " \"x\": \"%.*g\",\n" 590 " \"y\": \"%.*g\"\n },\n", 591 GetMagickPrecision(),channel_moments[channel].centroid.x, 592 GetMagickPrecision(),channel_moments[channel].centroid.y); 593 n+=FormatLocaleFile(file," \"ellipseSemiMajorMinorAxis\": {\n" 594 " \"x\": \"%.*g\",\n" 595 " \"y\": \"%.*g\"\n },\n", 596 GetMagickPrecision(),channel_moments[channel].ellipse_axis.x, 597 GetMagickPrecision(),channel_moments[channel].ellipse_axis.y); 598 n+=FormatLocaleFile(file," \"ellipseAngle\": \"%.*g\",\n", 599 GetMagickPrecision(),channel_moments[channel].ellipse_angle); 600 n+=FormatLocaleFile(file," \"ellipseEccentricity\": \"%.*g\",\n", 601 GetMagickPrecision(),channel_moments[channel].ellipse_eccentricity); 602 n+=FormatLocaleFile(file," \"ellipseIntensity\": \"%.*g\",\n", 603 GetMagickPrecision(),channel_moments[channel].ellipse_intensity); 604 for (i=0; i < 7; i++) 605 n+=FormatLocaleFile(file," \"I%.20g\": \"%.*g\",\n",i+1.0, 606 GetMagickPrecision(),channel_moments[channel].invariant[i]); 607 n+=FormatLocaleFile(file," \"I%.20g\": \"%.*g\"\n",i+1.0, 608 GetMagickPrecision(),channel_moments[channel].invariant[i]); 609 (void) FormatLocaleFile(file," }"); 610 if (separator != MagickFalse) 611 (void) FormatLocaleFile(file,","); 612 (void) FormatLocaleFile(file,"\n"); 613 return(n); 614 } 615 616 static ssize_t PrintChannelPerceptualHash(FILE *file,const ChannelType channel, 617 const char *name,const MagickBooleanType separator, 618 const ChannelPerceptualHash *channel_phash) 619 { 620 register ssize_t 621 i; 622 623 ssize_t 624 n; 625 626 n=FormatLocaleFile(file," \"%s\": {\n",name); 627 for (i=0; i < 6; i++) 628 n+=FormatLocaleFile(file, 629 " \"PH%.20g\": [ \"%.*g\", \"%.*g\" ],\n",i+1.0, 630 GetMagickPrecision(),channel_phash[channel].srgb_hu_phash[i], 631 GetMagickPrecision(),channel_phash[channel].hclp_hu_phash[i]); 632 n+=FormatLocaleFile(file, 633 " \"PH%.20g\": [ \"%.*g\", \"%.*g\" ]\n",i+1.0, 634 GetMagickPrecision(),channel_phash[channel].srgb_hu_phash[i], 635 GetMagickPrecision(),channel_phash[channel].hclp_hu_phash[i]); 636 (void) FormatLocaleFile(file," }"); 637 if (separator != MagickFalse) 638 (void) FormatLocaleFile(file,","); 639 (void) FormatLocaleFile(file,"\n"); 640 return(n); 641 } 642 643 static ssize_t PrintChannelStatistics(FILE *file,const PixelChannel channel, 644 const char *name,const double scale,const MagickBooleanType separator, 645 const ChannelStatistics *channel_statistics) 646 { 647 #define StatisticsFormat " \"%s\": {\n \"min\": \"" QuantumFormat \ 648 "\",\n \"max\": \"" QuantumFormat "\",\n" \ 649 " \"mean\": \"%g\",\n \"standardDeviation\": " \ 650 "\"%g\",\n \"kurtosis\": \"%g\",\n \"skewness\": " \ 651 "\"%g\"\n }" 652 653 ssize_t 654 n; 655 656 n=FormatLocaleFile(file,StatisticsFormat,name,ClampToQuantum(scale* 657 channel_statistics[channel].minima),ClampToQuantum(scale* 658 channel_statistics[channel].maxima),scale*channel_statistics[channel].mean, 659 scale*channel_statistics[channel].standard_deviation, 660 channel_statistics[channel].kurtosis,channel_statistics[channel].skewness); 661 if (separator != MagickFalse) 662 (void) FormatLocaleFile(file,","); 663 (void) FormatLocaleFile(file,"\n"); 664 return(n); 665 } 666 667 static MagickBooleanType EncodeImageAttributes(Image *image,FILE *file, 668 ExceptionInfo *exception) 669 { 670 char 671 color[MagickPathExtent], 672 format[MagickPathExtent], 673 key[MagickPathExtent]; 674 675 ChannelFeatures 676 *channel_features; 677 678 ChannelMoments 679 *channel_moments; 680 681 ChannelPerceptualHash 682 *channel_phash; 683 684 ChannelStatistics 685 *channel_statistics; 686 687 ColorspaceType 688 colorspace; 689 690 const char 691 *artifact, 692 *locate, 693 *name, 694 *property, 695 *registry, 696 *value; 697 698 const MagickInfo 699 *magick_info; 700 701 double 702 elapsed_time, 703 user_time; 704 705 ImageType 706 base_type, 707 type; 708 709 MagickBooleanType 710 ping; 711 712 register const Quantum 713 *p; 714 715 register ssize_t 716 i, 717 x; 718 719 size_t 720 depth, 721 distance, 722 scale; 723 724 ssize_t 725 y; 726 727 assert(image != (Image *) NULL); 728 assert(image->signature == MagickCoreSignature); 729 if (image->debug != MagickFalse) 730 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 731 *format='\0'; 732 elapsed_time=GetElapsedTime(&image->timer); 733 user_time=GetUserTime(&image->timer); 734 GetTimerInfo(&image->timer); 735 p=GetVirtualPixels(image,0,0,1,1,exception); 736 ping=p == (const Quantum *) NULL ? MagickTrue : MagickFalse; 737 (void) ping; 738 (void) SignatureImage(image,exception); 739 JsonFormatLocaleFile(file,"{\n \"image\": {\n \"name\": %s,\n", 740 image->filename); 741 if (*image->magick_filename != '\0') 742 if (LocaleCompare(image->magick_filename,image->filename) != 0) 743 { 744 char 745 filename[MagickPathExtent]; 746 747 GetPathComponent(image->magick_filename,TailPath,filename); 748 JsonFormatLocaleFile(file," \"baseName\": %s,\n",filename); 749 } 750 JsonFormatLocaleFile(file," \"format\": %s,\n",image->magick); 751 magick_info=GetMagickInfo(image->magick,exception); 752 if ((magick_info != (const MagickInfo *) NULL) && 753 (GetMagickDescription(magick_info) != (const char *) NULL)) 754 JsonFormatLocaleFile(file," \"formatDescription\": %s,\n", 755 image->magick); 756 if ((magick_info != (const MagickInfo *) NULL) && 757 (GetMagickMimeType(magick_info) != (const char *) NULL)) 758 JsonFormatLocaleFile(file," \"mimeType\": %s,\n",GetMagickMimeType( 759 magick_info)); 760 JsonFormatLocaleFile(file," \"class\": %s,\n",CommandOptionToMnemonic( 761 MagickClassOptions,(ssize_t) image->storage_class)); 762 (void) FormatLocaleFile(file," \"geometry\": {\n" 763 " \"width\": %.20g,\n \"height\": %.20g,\n" 764 " \"x\": %.20g,\n \"y\": %.20g\n },\n", 765 (double) image->columns,(double) image->rows,(double) image->tile_offset.x, 766 (double) image->tile_offset.y); 767 if ((image->magick_columns != 0) || (image->magick_rows != 0)) 768 if ((image->magick_columns != image->columns) || 769 (image->magick_rows != image->rows)) 770 (void) FormatLocaleFile(file," \"baseGeometry\": {\n" 771 " \"width\": %.20g,\n \"height\": %.20g\n },\n", 772 (double) image->magick_columns,(double) image->magick_rows); 773 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0)) 774 { 775 (void) FormatLocaleFile(file," \"resolution\": {\n" 776 " \"x\": %.20g,\n \"y\": %.20g\n },\n", 777 image->resolution.x,image->resolution.y); 778 (void) FormatLocaleFile(file," \"printSize\": {\n" 779 " \"x\": %.20g,\n \"y\": %.20g\n },\n", 780 image->columns/image->resolution.x,(double) image->rows/ 781 image->resolution.y); 782 } 783 JsonFormatLocaleFile(file," \"units\": %s,\n",CommandOptionToMnemonic( 784 MagickResolutionOptions,(ssize_t) image->units)); 785 colorspace=image->colorspace; 786 type=IdentifyImageType(image,exception); 787 if ((type == BilevelType) || (type == GrayscaleType) || 788 (type == GrayscaleAlphaType)) 789 colorspace=GRAYColorspace; 790 JsonFormatLocaleFile(file," \"type\": %s,\n",CommandOptionToMnemonic( 791 MagickTypeOptions,(ssize_t) type)); 792 base_type=GetImageType(image); 793 if (type != base_type) 794 JsonFormatLocaleFile(file," \"baseType\": %s,\n", 795 CommandOptionToMnemonic(MagickTypeOptions,(ssize_t) base_type)); 796 JsonFormatLocaleFile(file," \"endianess\": %s,\n", 797 CommandOptionToMnemonic(MagickEndianOptions,(ssize_t) image->endian)); 798 locate=GetImageArtifact(image,"identify:locate"); 799 if (locate == (const char *) NULL) 800 locate=GetImageArtifact(image,"json:locate"); 801 if (locate != (const char *) NULL) 802 { 803 const char 804 *limit; 805 806 size_t 807 max_locations; 808 809 StatisticType 810 type; 811 812 /* 813 Display minimum, maximum, or mean pixel locations. 814 */ 815 type=(StatisticType) ParseCommandOption(MagickStatisticOptions, 816 MagickFalse,locate); 817 limit=GetImageArtifact(image,"identify:limit"); 818 if (limit == (const char *) NULL) 819 limit=GetImageArtifact(image,"json:limit"); 820 max_locations=0; 821 if (limit != (const char *) NULL) 822 max_locations=StringToUnsignedLong(limit); 823 channel_statistics=GetLocationStatistics(image,type,exception); 824 if (channel_statistics == (ChannelStatistics *) NULL) 825 return(MagickFalse); 826 (void) FormatLocaleFile(file," \"channel%s\": {\n",locate); 827 if (image->alpha_trait != UndefinedPixelTrait) 828 (void) PrintChannelLocations(file,image,AlphaPixelChannel,"Alpha", 829 type,max_locations,MagickTrue,channel_statistics); 830 switch (colorspace) 831 { 832 case RGBColorspace: 833 default: 834 { 835 (void) PrintChannelLocations(file,image,RedPixelChannel,"Red", 836 type,max_locations,MagickTrue,channel_statistics); 837 (void) PrintChannelLocations(file,image,GreenPixelChannel,"Green", 838 type,max_locations,MagickTrue,channel_statistics); 839 (void) PrintChannelLocations(file,image,BluePixelChannel,"Blue", 840 type,max_locations,MagickFalse,channel_statistics); 841 break; 842 } 843 case CMYKColorspace: 844 { 845 (void) PrintChannelLocations(file,image,CyanPixelChannel,"Cyan", 846 type,max_locations,MagickTrue,channel_statistics); 847 (void) PrintChannelLocations(file,image,MagentaPixelChannel,"Magenta", 848 type,max_locations,MagickTrue,channel_statistics); 849 (void) PrintChannelLocations(file,image,YellowPixelChannel,"Yellow", 850 type,max_locations,MagickTrue,channel_statistics); 851 (void) PrintChannelLocations(file,image,BlackPixelChannel,"Black", 852 type,max_locations,MagickFalse,channel_statistics); 853 break; 854 } 855 case GRAYColorspace: 856 { 857 (void) PrintChannelLocations(file,image,GrayPixelChannel,"Gray", 858 type,max_locations,MagickFalse,channel_statistics); 859 break; 860 } 861 } 862 (void) FormatLocaleFile(file," },\n"); 863 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory( 864 channel_statistics); 865 } 866 /* 867 Detail channel depth and extrema. 868 */ 869 JsonFormatLocaleFile(file," \"colorspace\": %s,\n", 870 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) 871 image->colorspace)); 872 channel_statistics=(ChannelStatistics *) NULL; 873 channel_moments=(ChannelMoments *) NULL; 874 channel_phash=(ChannelPerceptualHash *) NULL; 875 channel_features=(ChannelFeatures *) NULL; 876 scale=1; 877 channel_statistics=GetImageStatistics(image,exception); 878 if (channel_statistics == (ChannelStatistics *) NULL) 879 return(MagickFalse); 880 artifact=GetImageArtifact(image,"identify:moments"); 881 if (artifact == (const char *) NULL) 882 artifact=GetImageArtifact(image,"json:moments"); 883 if (artifact != (const char *) NULL) 884 { 885 channel_moments=GetImageMoments(image,exception); 886 channel_phash=GetImagePerceptualHash(image,exception); 887 } 888 artifact=GetImageArtifact(image,"identify:features"); 889 if (artifact == (const char *) NULL) 890 artifact=GetImageArtifact(image,"json:features"); 891 if (artifact != (const char *) NULL) 892 { 893 distance=StringToUnsignedLong(artifact); 894 channel_features=GetImageFeatures(image,distance,exception); 895 } 896 depth=GetImageDepth(image,exception); 897 (void) FormatLocaleFile(file," \"depth\": %.20g,\n",(double) depth); 898 (void) FormatLocaleFile(file," \"baseDepth\": %.20g,\n",(double) 899 image->depth); 900 (void) FormatLocaleFile(file," \"channelDepth\": {\n"); 901 if (image->alpha_trait != UndefinedPixelTrait) 902 (void) FormatLocaleFile(file," \"alpha\": %.20g,\n",(double) 903 channel_statistics[AlphaPixelChannel].depth); 904 switch (colorspace) 905 { 906 case RGBColorspace: 907 default: 908 { 909 (void) FormatLocaleFile(file," \"red\": %.20g,\n",(double) 910 channel_statistics[RedChannel].depth); 911 (void) FormatLocaleFile(file," \"green\": %.20g,\n",(double) 912 channel_statistics[GreenChannel].depth); 913 (void) FormatLocaleFile(file," \"blue\": %.20g\n",(double) 914 channel_statistics[BlueChannel].depth); 915 break; 916 } 917 case CMYKColorspace: 918 { 919 (void) FormatLocaleFile(file," \"cyan\": %.20g,\n",(double) 920 channel_statistics[CyanChannel].depth); 921 (void) FormatLocaleFile(file," \"magenta\": %.20g,\n",(double) 922 channel_statistics[MagentaChannel].depth); 923 (void) FormatLocaleFile(file," \"yellow\": %.20g,\n",(double) 924 channel_statistics[YellowChannel].depth); 925 (void) FormatLocaleFile(file," \"black\": %.20g\n",(double) 926 channel_statistics[BlackChannel].depth); 927 break; 928 } 929 case GRAYColorspace: 930 { 931 (void) FormatLocaleFile(file," \"gray\": %.20g\n",(double) 932 channel_statistics[GrayChannel].depth); 933 break; 934 } 935 } 936 (void) FormatLocaleFile(file," },\n"); 937 scale=1; 938 if (image->depth <= MAGICKCORE_QUANTUM_DEPTH) 939 scale=QuantumRange/((size_t) QuantumRange >> ((size_t) 940 MAGICKCORE_QUANTUM_DEPTH-image->depth)); 941 if (channel_statistics != (ChannelStatistics *) NULL) 942 { 943 (void) FormatLocaleFile(file," \"pixels\": %.20g,\n", 944 channel_statistics[CompositePixelChannel].area); 945 if (colorspace != GRAYColorspace) 946 { 947 (void) FormatLocaleFile(file," \"imageStatistics\": {\n"); 948 (void) PrintChannelStatistics(file,(PixelChannel) MaxPixelChannels, 949 "Overall",1.0/scale,MagickFalse,channel_statistics); 950 (void) FormatLocaleFile(file," },\n"); 951 } 952 (void) FormatLocaleFile(file," \"channelStatistics\": {\n"); 953 if (image->alpha_trait != UndefinedPixelTrait) 954 (void) PrintChannelStatistics(file,AlphaPixelChannel,"Alpha",1.0/scale, 955 MagickTrue,channel_statistics); 956 switch (colorspace) 957 { 958 case RGBColorspace: 959 default: 960 { 961 (void) PrintChannelStatistics(file,RedPixelChannel,"Red",1.0/scale, 962 MagickTrue,channel_statistics); 963 (void) PrintChannelStatistics(file,GreenPixelChannel,"Green",1.0/ 964 scale,MagickTrue,channel_statistics); 965 (void) PrintChannelStatistics(file,BluePixelChannel,"Blue",1.0/scale, 966 MagickFalse,channel_statistics); 967 break; 968 } 969 case CMYKColorspace: 970 { 971 (void) PrintChannelStatistics(file,CyanPixelChannel,"Cyan",1.0/scale, 972 MagickTrue,channel_statistics); 973 (void) PrintChannelStatistics(file,MagentaPixelChannel,"Magenta",1.0/ 974 scale,MagickTrue,channel_statistics); 975 (void) PrintChannelStatistics(file,YellowPixelChannel,"Yellow",1.0/ 976 scale,MagickTrue,channel_statistics); 977 (void) PrintChannelStatistics(file,BlackPixelChannel,"Black",1.0/ 978 scale,MagickFalse,channel_statistics); 979 break; 980 } 981 case GRAYColorspace: 982 { 983 (void) PrintChannelStatistics(file,GrayPixelChannel,"Gray",1.0/scale, 984 MagickFalse,channel_statistics); 985 break; 986 } 987 } 988 (void) FormatLocaleFile(file," },\n"); 989 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory( 990 channel_statistics); 991 } 992 if (channel_moments != (ChannelMoments *) NULL) 993 { 994 (void) FormatLocaleFile(file," \"channelMoments\": {\n"); 995 if (image->alpha_trait != UndefinedPixelTrait) 996 (void) PrintChannelMoments(file,AlphaPixelChannel,"Alpha",MagickTrue, 997 channel_moments); 998 switch (colorspace) 999 { 1000 case RGBColorspace: 1001 default: 1002 { 1003 (void) PrintChannelMoments(file,RedPixelChannel,"Red",MagickTrue, 1004 channel_moments); 1005 (void) PrintChannelMoments(file,GreenPixelChannel,"Green",MagickTrue, 1006 channel_moments); 1007 (void) PrintChannelMoments(file,BluePixelChannel,"Blue",MagickFalse, 1008 channel_moments); 1009 break; 1010 } 1011 case CMYKColorspace: 1012 { 1013 (void) PrintChannelMoments(file,CyanPixelChannel,"Cyan",MagickTrue, 1014 channel_moments); 1015 (void) PrintChannelMoments(file,MagentaPixelChannel,"Magenta", 1016 MagickTrue,channel_moments); 1017 (void) PrintChannelMoments(file,YellowPixelChannel,"Yellow", 1018 MagickTrue,channel_moments); 1019 (void) PrintChannelMoments(file,BlackPixelChannel,"Black", 1020 MagickFalse,channel_moments); 1021 break; 1022 } 1023 case GRAYColorspace: 1024 { 1025 (void) PrintChannelMoments(file,GrayPixelChannel,"Gray",MagickFalse, 1026 channel_moments); 1027 break; 1028 } 1029 } 1030 (void) FormatLocaleFile(file," },\n"); 1031 channel_moments=(ChannelMoments *) RelinquishMagickMemory( 1032 channel_moments); 1033 } 1034 if (channel_phash != (ChannelPerceptualHash *) NULL) 1035 { 1036 (void) FormatLocaleFile(file," \"channelPerceptualHash\": {\n"); 1037 if (image->alpha_trait != UndefinedPixelTrait) 1038 (void) PrintChannelPerceptualHash(file,AlphaChannel,"alphaAlpha", 1039 MagickTrue,channel_phash); 1040 (void) PrintChannelPerceptualHash(file,RedChannel,"redHue",MagickTrue, 1041 channel_phash); 1042 (void) PrintChannelPerceptualHash(file,GreenChannel,"greenChroma", 1043 MagickTrue,channel_phash); 1044 (void) PrintChannelPerceptualHash(file,BlueChannel,"blueLuma",MagickFalse, 1045 channel_phash); 1046 (void) FormatLocaleFile(file," },\n"); 1047 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory( 1048 channel_phash); 1049 } 1050 if (channel_features != (ChannelFeatures *) NULL) 1051 { 1052 (void) FormatLocaleFile(file," \"channelFeatures\": {\n"); 1053 if (image->alpha_trait != UndefinedPixelTrait) 1054 (void) PrintChannelFeatures(file,AlphaPixelChannel,"Alpha",MagickTrue, 1055 channel_features); 1056 switch (colorspace) 1057 { 1058 case RGBColorspace: 1059 default: 1060 { 1061 (void) PrintChannelFeatures(file,RedPixelChannel,"Red",MagickTrue, 1062 channel_features); 1063 (void) PrintChannelFeatures(file,GreenPixelChannel,"Green", 1064 MagickTrue,channel_features); 1065 (void) PrintChannelFeatures(file,BluePixelChannel,"Blue",MagickFalse, 1066 channel_features); 1067 break; 1068 } 1069 case CMYKColorspace: 1070 { 1071 (void) PrintChannelFeatures(file,CyanPixelChannel,"Cyan",MagickTrue, 1072 channel_features); 1073 (void) PrintChannelFeatures(file,MagentaPixelChannel,"Magenta", 1074 MagickTrue,channel_features); 1075 (void) PrintChannelFeatures(file,YellowPixelChannel,"Yellow", 1076 MagickTrue,channel_features); 1077 (void) PrintChannelFeatures(file,BlackPixelChannel,"Black", 1078 MagickFalse,channel_features); 1079 break; 1080 } 1081 case GRAYColorspace: 1082 { 1083 (void) PrintChannelFeatures(file,GrayPixelChannel,"Gray",MagickFalse, 1084 channel_features); 1085 break; 1086 } 1087 } 1088 (void) FormatLocaleFile(file," },\n"); 1089 channel_features=(ChannelFeatures *) RelinquishMagickMemory( 1090 channel_features); 1091 } 1092 if (image->colorspace == CMYKColorspace) 1093 (void) FormatLocaleFile(file," \"totalInkDensity\": \"%.*g%%\",\n", 1094 GetMagickPrecision(),100.0*GetImageTotalInkDensity(image,exception)/ 1095 (double) QuantumRange); 1096 x=0; 1097 if (image->alpha_trait != UndefinedPixelTrait) 1098 { 1099 register const Quantum 1100 *p; 1101 1102 p=(const Quantum *) NULL; 1103 for (y=0; y < (ssize_t) image->rows; y++) 1104 { 1105 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 1106 if (p == (const Quantum *) NULL) 1107 break; 1108 for (x=0; x < (ssize_t) image->columns; x++) 1109 { 1110 if (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha) 1111 break; 1112 p+=GetPixelChannels(image); 1113 } 1114 if (x < (ssize_t) image->columns) 1115 break; 1116 } 1117 if ((x < (ssize_t) image->columns) || (y < (ssize_t) image->rows)) 1118 { 1119 PixelInfo 1120 pixel; 1121 1122 GetPixelInfo(image,&pixel); 1123 GetPixelInfoPixel(image,p,&pixel); 1124 GetColorTuple(&pixel,MagickTrue,color); 1125 (void) FormatLocaleFile(file," \"alpha\": \"%s\",\n",color); 1126 } 1127 } 1128 if (image->storage_class == PseudoClass) 1129 { 1130 register PixelInfo 1131 *magick_restrict p; 1132 1133 (void) FormatLocaleFile(file," \"colormapEntries\": %.20g,\n", 1134 (double) image->colors); 1135 (void) FormatLocaleFile(file," \"colormap\": [\n "); 1136 p=image->colormap; 1137 for (i=0; i < (ssize_t) image->colors; i++) 1138 { 1139 GetColorTuple(p,MagickTrue,color); 1140 (void) FormatLocaleFile(file,"\"%s\"",color); 1141 if (i < (ssize_t) (image->colors-1)) 1142 (void) FormatLocaleFile(file,","); 1143 if (((i+1) % 5) == 0) 1144 (void) FormatLocaleFile(file,"\n "); 1145 p++; 1146 } 1147 (void) FormatLocaleFile(file,"\n ],\n"); 1148 } 1149 if (image->error.mean_error_per_pixel != 0.0) 1150 (void) FormatLocaleFile(file," \"meanErrorPerPixel\": \"%g\",\n", 1151 image->error.mean_error_per_pixel); 1152 if (image->error.normalized_mean_error != 0.0) 1153 (void) FormatLocaleFile(file," \"normalizedMeanError\": \"%g\",\n", 1154 image->error.normalized_mean_error); 1155 if (image->error.normalized_maximum_error != 0.0) 1156 (void) FormatLocaleFile(file," \"normalizedMaximumError\": \"%g\",\n", 1157 image->error.normalized_maximum_error); 1158 JsonFormatLocaleFile(file," \"renderingIntent\": %s,\n", 1159 CommandOptionToMnemonic(MagickIntentOptions,(ssize_t) 1160 image->rendering_intent)); 1161 if (image->gamma != 0.0) 1162 (void) FormatLocaleFile(file," \"gamma\": %g,\n",image->gamma); 1163 if ((image->chromaticity.red_primary.x != 0.0) || 1164 (image->chromaticity.green_primary.x != 0.0) || 1165 (image->chromaticity.blue_primary.x != 0.0) || 1166 (image->chromaticity.white_point.x != 0.0)) 1167 { 1168 /* 1169 Display image chromaticity. 1170 */ 1171 (void) FormatLocaleFile(file," \"chromaticity\": {\n"); 1172 (void) FormatLocaleFile(file," \"redPrimary\": {\n" 1173 " \"x\": %g,\n \"y\": %g\n },\n", 1174 image->chromaticity.red_primary.x,image->chromaticity.red_primary.y); 1175 (void) FormatLocaleFile(file," \"greenPrimary\": {\n" 1176 " \"x\": %g,\n \"y\": %g\n },\n", 1177 image->chromaticity.green_primary.x, 1178 image->chromaticity.green_primary.y); 1179 (void) FormatLocaleFile(file," \"bluePrimary\": {\n" 1180 " \"x\": %g,\n \"y\": %g\n },\n", 1181 image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y); 1182 (void) FormatLocaleFile(file," \"whitePrimary\": {\n" 1183 " \"x\": %g,\n \"y\": %g\n }\n", 1184 image->chromaticity.white_point.x,image->chromaticity.white_point.y); 1185 (void) FormatLocaleFile(file," },\n"); 1186 } 1187 if ((image->extract_info.width*image->extract_info.height) != 0) 1188 (void) FormatLocaleFile(file," \"tileGeometry\": {\n" 1189 " \"width\": %.20g,\n \"height\": %.20g,\n" 1190 " \"x\": %.20g,\n \"y\": %.20g\n },\n", 1191 (double) image->extract_info.width,(double) image->extract_info.height, 1192 (double) image->extract_info.x,(double) image->extract_info.y); 1193 GetColorTuple(&image->alpha_color,MagickTrue,color); 1194 (void) FormatLocaleFile(file," \"alphaColor\": \"%s\",\n",color); 1195 GetColorTuple(&image->background_color,MagickTrue,color); 1196 (void) FormatLocaleFile(file," \"backgroundColor\": \"%s\",\n",color); 1197 GetColorTuple(&image->border_color,MagickTrue,color); 1198 (void) FormatLocaleFile(file," \"borderColor\": \"%s\",\n",color); 1199 GetColorTuple(&image->transparent_color,MagickTrue,color); 1200 (void) FormatLocaleFile(file," \"transparentColor\": \"%s\",\n",color); 1201 JsonFormatLocaleFile(file," \"interlace\": %s,\n",CommandOptionToMnemonic( 1202 MagickInterlaceOptions,(ssize_t) image->interlace)); 1203 JsonFormatLocaleFile(file," \"intensity\": %s,\n",CommandOptionToMnemonic( 1204 MagickPixelIntensityOptions,(ssize_t) image->intensity)); 1205 JsonFormatLocaleFile(file," \"compose\": %s,\n", 1206 CommandOptionToMnemonic(MagickComposeOptions,(ssize_t) image->compose)); 1207 if ((image->page.width != 0) || (image->page.height != 0) || 1208 (image->page.x != 0) || (image->page.y != 0)) 1209 (void) FormatLocaleFile(file," \"pageGeometry\": {\n" 1210 " \"width\": %.20g,\n \"height\": %.20g,\n" 1211 " \"x\": %.20g,\n \"y\": %.20g\n },\n", 1212 (double) image->page.width,(double) image->page.height, 1213 (double) image->page.x,(double) image->page.y); 1214 if ((image->page.x != 0) || (image->page.y != 0)) 1215 (void) FormatLocaleFile(file," \"originGeometry\": %+.20g%+.20g\n", 1216 (double) image->page.x,(double) image->page.y); 1217 JsonFormatLocaleFile(file," \"dispose\": %s,\n", 1218 CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) image->dispose)); 1219 if (image->delay != 0) 1220 (void) FormatLocaleFile(file," \"delay\": \"%.20gx%.20g\"\n", 1221 (double) image->delay,(double) image->ticks_per_second); 1222 if (image->iterations != 1) 1223 (void) FormatLocaleFile(file," \"iterations\": %.20g,\n",(double) 1224 image->iterations); 1225 if ((image->next != (Image *) NULL) || (image->previous != (Image *) NULL)) 1226 (void) FormatLocaleFile(file," \"scene\": %.20g\n \"scenes\": " 1227 "%.20g\n",(double) image->scene,(double) GetImageListLength(image)); 1228 else 1229 if (image->scene != 0) 1230 (void) FormatLocaleFile(file," \"scene\": %.20g,\n",(double) 1231 image->scene); 1232 JsonFormatLocaleFile(file," \"compression\": %s,\n", 1233 CommandOptionToMnemonic(MagickCompressOptions,(ssize_t) 1234 image->compression)); 1235 if (image->quality != UndefinedCompressionQuality) 1236 (void) FormatLocaleFile(file," \"quality\": %.20g,\n",(double) 1237 image->quality); 1238 JsonFormatLocaleFile(file," \"orientation\": %s,\n", 1239 CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t) 1240 image->orientation)); 1241 if (image->montage != (char *) NULL) 1242 JsonFormatLocaleFile(file," \"montage\": \"%s\",\n",image->montage); 1243 if (image->directory != (char *) NULL) 1244 { 1245 Image 1246 *tile; 1247 1248 ImageInfo 1249 *image_info; 1250 1251 register char 1252 *p, 1253 *q; 1254 1255 WarningHandler 1256 handler; 1257 1258 /* 1259 Display visual image directory. 1260 */ 1261 image_info=AcquireImageInfo(); 1262 (void) CloneString(&image_info->size,"64x64"); 1263 (void) FormatLocaleFile(file," \"montageDirectory\": ["); 1264 p=image->directory; 1265 while (*p != '\0') 1266 { 1267 q=p; 1268 while ((*q != '\n') && (*q != '\0')) 1269 q++; 1270 (void) CopyMagickString(image_info->filename,p,(size_t) (q-p+1)); 1271 p=q+1; 1272 JsonFormatLocaleFile(file,"{\n \"name\": %s", 1273 image_info->filename); 1274 handler=SetWarningHandler((WarningHandler) NULL); 1275 tile=ReadImage(image_info,exception); 1276 (void) SetWarningHandler(handler); 1277 if (tile == (Image *) NULL) 1278 { 1279 (void) FormatLocaleFile(file," }"); 1280 continue; 1281 } 1282 (void) FormatLocaleFile(file,",\n \"info\": \"%.20gx%.20g %s\"", 1283 (double) tile->magick_columns,(double) tile->magick_rows, 1284 tile->magick); 1285 (void) SignatureImage(tile,exception); 1286 ResetImagePropertyIterator(tile); 1287 property=GetNextImageProperty(tile); 1288 while (property != (const char *) NULL) 1289 { 1290 JsonFormatLocaleFile(file,",\n %s: ",property); 1291 value=GetImageProperty(tile,property,exception); 1292 JsonFormatLocaleFile(file,"%s",value); 1293 property=GetNextImageProperty(tile); 1294 } 1295 tile=DestroyImage(tile); 1296 if (*p != '\0') 1297 (void) FormatLocaleFile(file,"\n },"); 1298 else 1299 (void) FormatLocaleFile(file,"\n }"); 1300 } 1301 (void) FormatLocaleFile(file,"],\n"); 1302 image_info=DestroyImageInfo(image_info); 1303 } 1304 (void) GetImageProperty(image,"exif:*",exception); 1305 (void) GetImageProperty(image,"icc:*",exception); 1306 (void) GetImageProperty(image,"iptc:*",exception); 1307 (void) GetImageProperty(image,"xmp:*",exception); 1308 ResetImagePropertyIterator(image); 1309 property=GetNextImageProperty(image); 1310 if (property != (const char *) NULL) 1311 { 1312 size_t 1313 n; 1314 1315 /* 1316 Display image properties. 1317 */ 1318 n=0; 1319 (void) FormatLocaleFile(file," \"properties\": {\n"); 1320 while (property != (const char *) NULL) 1321 { 1322 if (n++ != 0) 1323 (void) FormatLocaleFile(file,",\n"); 1324 JsonFormatLocaleFile(file," %s: ",property); 1325 value=GetImageProperty(image,property,exception); 1326 JsonFormatLocaleFile(file,"%s",value); 1327 property=GetNextImageProperty(image); 1328 } 1329 (void) FormatLocaleFile(file,"\n },\n"); 1330 } 1331 (void) FormatLocaleString(key,MagickPathExtent,"8BIM:1999,2998:#1"); 1332 value=GetImageProperty(image,key,exception); 1333 if (value != (const char *) NULL) 1334 { 1335 /* 1336 Display clipping path. 1337 */ 1338 (void) FormatLocaleFile(file," \"clipping path\": {\n"); 1339 JsonFormatLocaleFile(file,"%s\n",value); 1340 (void) FormatLocaleFile(file," },\n"); 1341 } 1342 ResetImageProfileIterator(image); 1343 name=GetNextImageProfile(image); 1344 if (name != (char *) NULL) 1345 { 1346 const StringInfo 1347 *profile; 1348 1349 size_t 1350 n; 1351 1352 /* 1353 Identify image profiles. 1354 */ 1355 n=0; 1356 (void) FormatLocaleFile(file," \"profiles\": {\n"); 1357 while (name != (char *) NULL) 1358 { 1359 profile=GetImageProfile(image,name); 1360 if (profile == (StringInfo *) NULL) 1361 continue; 1362 if (n++ != 0) 1363 (void) FormatLocaleFile(file,",\n"); 1364 JsonFormatLocaleFile(file," %s: {\n",name); 1365 if (LocaleCompare(name,"iptc") == 0) 1366 { 1367 char 1368 *attribute, 1369 **attribute_list; 1370 1371 const char 1372 *tag; 1373 1374 long 1375 dataset, 1376 record, 1377 sentinel; 1378 1379 register ssize_t 1380 j; 1381 1382 size_t 1383 length, 1384 profile_length; 1385 1386 profile_length=GetStringInfoLength(profile); 1387 for (i=0; i < (ssize_t) profile_length; i+=(ssize_t) length) 1388 { 1389 length=1; 1390 sentinel=GetStringInfoDatum(profile)[i++]; 1391 if (sentinel != 0x1c) 1392 continue; 1393 dataset=GetStringInfoDatum(profile)[i++]; 1394 record=GetStringInfoDatum(profile)[i++]; 1395 switch (record) 1396 { 1397 case 5: tag="Image Name"; break; 1398 case 7: tag="Edit Status"; break; 1399 case 10: tag="Priority"; break; 1400 case 15: tag="Category"; break; 1401 case 20: tag="Supplemental Category"; break; 1402 case 22: tag="Fixture Identifier"; break; 1403 case 25: tag="Keyword"; break; 1404 case 30: tag="Release Date"; break; 1405 case 35: tag="Release Time"; break; 1406 case 40: tag="Special Instructions"; break; 1407 case 45: tag="Reference Service"; break; 1408 case 47: tag="Reference Date"; break; 1409 case 50: tag="Reference Number"; break; 1410 case 55: tag="Created Date"; break; 1411 case 60: tag="Created Time"; break; 1412 case 65: tag="Originating Program"; break; 1413 case 70: tag="Program Version"; break; 1414 case 75: tag="Object Cycle"; break; 1415 case 80: tag="Byline"; break; 1416 case 85: tag="Byline Title"; break; 1417 case 90: tag="City"; break; 1418 case 92: tag="Sub-Location"; break; 1419 case 95: tag="Province State"; break; 1420 case 100: tag="Country Code"; break; 1421 case 101: tag="Country"; break; 1422 case 103: tag="Original Transmission Reference"; break; 1423 case 105: tag="Headline"; break; 1424 case 110: tag="Credit"; break; 1425 case 115: tag="Src"; break; 1426 case 116: tag="Copyright String"; break; 1427 case 120: tag="Caption"; break; 1428 case 121: tag="Local Caption"; break; 1429 case 122: tag="Caption Writer"; break; 1430 case 200: tag="Custom Field 1"; break; 1431 case 201: tag="Custom Field 2"; break; 1432 case 202: tag="Custom Field 3"; break; 1433 case 203: tag="Custom Field 4"; break; 1434 case 204: tag="Custom Field 5"; break; 1435 case 205: tag="Custom Field 6"; break; 1436 case 206: tag="Custom Field 7"; break; 1437 case 207: tag="Custom Field 8"; break; 1438 case 208: tag="Custom Field 9"; break; 1439 case 209: tag="Custom Field 10"; break; 1440 case 210: tag="Custom Field 11"; break; 1441 case 211: tag="Custom Field 12"; break; 1442 case 212: tag="Custom Field 13"; break; 1443 case 213: tag="Custom Field 14"; break; 1444 case 214: tag="Custom Field 15"; break; 1445 case 215: tag="Custom Field 16"; break; 1446 case 216: tag="Custom Field 17"; break; 1447 case 217: tag="Custom Field 18"; break; 1448 case 218: tag="Custom Field 19"; break; 1449 case 219: tag="Custom Field 20"; break; 1450 default: tag="unknown"; break; 1451 } 1452 (void) FormatLocaleFile(file," %s[%.20g,%.20g]: ",tag, 1453 (double) dataset,(double) record); 1454 length=(size_t) (GetStringInfoDatum(profile)[i++] << 8); 1455 length|=GetStringInfoDatum(profile)[i++]; 1456 attribute=(char *) NULL; 1457 if (~length >= (MagickPathExtent-1)) 1458 attribute=(char *) AcquireQuantumMemory(length+MagickPathExtent, 1459 sizeof(*attribute)); 1460 if (attribute != (char *) NULL) 1461 { 1462 (void) CopyMagickString(attribute,(char *) 1463 GetStringInfoDatum(profile)+i,length+1); 1464 attribute_list=StringToList(attribute); 1465 if (attribute_list != (char **) NULL) 1466 { 1467 (void) FormatLocaleFile(file,"["); 1468 for (j=0; attribute_list[j] != (char *) NULL; j++) 1469 { 1470 if (j != 0) 1471 (void) FormatLocaleFile(file,","); 1472 JsonFormatLocaleFile(file,"%s",attribute_list[j]); 1473 attribute_list[j]=(char *) RelinquishMagickMemory( 1474 attribute_list[j]); 1475 } 1476 (void) FormatLocaleFile(file,"],"); 1477 attribute_list=(char **) RelinquishMagickMemory( 1478 attribute_list); 1479 } 1480 else 1481 (void) FormatLocaleFile(file,"null,"); 1482 attribute=DestroyString(attribute); 1483 } 1484 else 1485 (void) FormatLocaleFile(file,"null,"); 1486 } 1487 } 1488 (void) FormatLocaleFile(file," \"length\": \"%.20g\"",(double) 1489 GetStringInfoLength(profile)); 1490 (void) FormatLocaleFile(file,"\n }"); 1491 name=GetNextImageProfile(image); 1492 } 1493 (void) FormatLocaleFile(file,"\n },\n"); 1494 } 1495 ResetImageArtifactIterator(image); 1496 artifact=GetNextImageArtifact(image); 1497 if (artifact != (const char *) NULL) 1498 { 1499 ssize_t 1500 n; 1501 1502 /* 1503 Display image artifacts. 1504 */ 1505 n=0; 1506 (void) FormatLocaleFile(file," \"artifacts\": {\n"); 1507 while (artifact != (const char *) NULL) 1508 { 1509 if (n++ != 0) 1510 (void) FormatLocaleFile(file,",\n"); 1511 JsonFormatLocaleFile(file," %s: ",artifact); 1512 value=GetImageArtifact(image,artifact); 1513 JsonFormatLocaleFile(file,"%s",value); 1514 artifact=GetNextImageArtifact(image); 1515 } 1516 (void) FormatLocaleFile(file,"\n },\n"); 1517 } 1518 ResetImageRegistryIterator(); 1519 registry=GetNextImageRegistry(); 1520 if (registry != (const char *) NULL) 1521 { 1522 ssize_t 1523 n; 1524 1525 /* 1526 Display image registry. 1527 */ 1528 (void) FormatLocaleFile(file," \"registry\": {\n"); 1529 n=0; 1530 while (registry != (const char *) NULL) 1531 { 1532 if (n++ != 0) 1533 (void) FormatLocaleFile(file,",\n"); 1534 JsonFormatLocaleFile(file," %s: ",registry); 1535 value=(const char *) GetImageRegistry(StringRegistryType,registry, 1536 exception); 1537 JsonFormatLocaleFile(file,"%s",value); 1538 registry=GetNextImageRegistry(); 1539 } 1540 (void) FormatLocaleFile(file," },\n"); 1541 } 1542 (void) FormatLocaleFile(file," \"tainted\": %s,\n", 1543 image->taint != MagickFalse ? "true" : "false"); 1544 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",MagickPathExtent, 1545 format); 1546 JsonFormatLocaleFile(file," \"filesize\": %s,\n",format); 1547 (void) FormatMagickSize((MagickSizeType) image->columns*image->rows, 1548 MagickFalse,"B",MagickPathExtent,format); 1549 if (strlen(format) > 1) 1550 format[strlen(format)-1]='\0'; 1551 JsonFormatLocaleFile(file," \"numberPixels\": %s,\n",format); 1552 (void) FormatMagickSize((MagickSizeType) ((double) image->columns*image->rows/ 1553 elapsed_time+0.5),MagickFalse,"B",MagickPathExtent,format); 1554 JsonFormatLocaleFile(file," \"pixelsPerSecond\": %s,\n",format); 1555 (void) FormatLocaleFile(file," \"userTime\": \"%0.3fu\",\n",user_time); 1556 (void) FormatLocaleFile(file," \"elapsedTime\": \"%lu:%02lu.%03lu\",\n", 1557 (unsigned long) (elapsed_time/60.0),(unsigned long) ceil(fmod( 1558 elapsed_time,60.0)),(unsigned long) (1000.0*(elapsed_time-floor( 1559 elapsed_time)))); 1560 JsonFormatLocaleFile(file," \"version\": %s\n", 1561 GetMagickVersion((size_t *) NULL)); 1562 (void) FormatLocaleFile(file," }\n}\n"); 1563 (void) fflush(file); 1564 return(ferror(file) != 0 ? MagickFalse : MagickTrue); 1565 } 1566 1567 static MagickBooleanType WriteJSONImage(const ImageInfo *image_info, 1568 Image *image,ExceptionInfo *exception) 1569 { 1570 FILE 1571 *file; 1572 1573 MagickBooleanType 1574 status; 1575 1576 MagickOffsetType 1577 scene; 1578 1579 /* 1580 Open output image file. 1581 */ 1582 assert(image_info != (const ImageInfo *) NULL); 1583 assert(image_info->signature == MagickCoreSignature); 1584 assert(image != (Image *) NULL); 1585 assert(image->signature == MagickCoreSignature); 1586 if (image->debug != MagickFalse) 1587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1588 status=OpenBlob(image_info,image,WriteBlobMode,exception); 1589 if (status == MagickFalse) 1590 return(status); 1591 file=GetBlobFileHandle(image); 1592 if (file == (FILE *) NULL) 1593 file=stdout; 1594 scene=0; 1595 do 1596 { 1597 WriteBlobString(image,"["); 1598 image->magick_columns=image->columns; 1599 image->magick_rows=image->rows; 1600 EncodeImageAttributes(image,file,exception); 1601 if (GetNextImageInList(image) == (Image *) NULL) 1602 { 1603 WriteBlobString(image,"]"); 1604 break; 1605 } 1606 WriteBlobString(image,",\n"); 1607 image=SyncNextImageInList(image); 1608 status=SetImageProgress(image,SaveImagesTag,scene++, 1609 GetImageListLength(image)); 1610 if (status == MagickFalse) 1611 break; 1612 } while (image_info->adjoin != MagickFalse); 1613 (void) CloseBlob(image); 1614 return(MagickTrue); 1615 } 1616