blah.
I assume that:
See also my Process modules page.
blah.
My programs follow certain conventions. They may be unusual, even idiosyncratic.
The source code of my standalone programs reside in %IMSRC%\snibgo. %IMSRC% is the IM v6 development directory, which varies with each version number. When I install a new version of IM, I copy the source code to the new directory. This means I always have a version of my code that works with previous versions of IM. This snibgo directory is at the same level in the IM directory structure as magick, wand and filters. blah.
There is also a %IM7SRC% directory, for v7 builds.
Q? HDRI? I would like Quantum and HDRI to be ImageMagick run-time options, instead of compile-time optons. Sadly, this isn't the case.
I don't publish binaries. If you want to use my standalone programs, you need to compile them.
Building. Make?
A basic recipe for building programs, starting from the CMD prompt, is:
cd %IMSRC%\snibgo bash $ export PKG_CONFIG_PATH=${IMDEV}../lib/pkgconfig $ cc -o wand wand.c `pkg-config --cflags --libs MagickWand` $ cp wand.exe ${IMDEV}
%IMDEV% (or, as bash calls it, ${IMDEV}) is my usual Q32 HDRI development directory. Insead of %IMDEV%, I could choose for example %IMG16i% or other Quantum and integer/float build.
Compiled binaries are copied to %IMDEV%.
echo %IMDEV%
C:\cygwin64\home\Alan\imdevins69940\bin\
For convenience, I put this in a bash script buildone.sh that I can call from Windows CMD, like this:
pushd %IMSRC%\snibgo bash buildone.sh wand hellowld snibconv swim bash buildwand.sh nlightest popd
When awkward processing is needed, more than is available from a single convert command, we can write a script, or a standalone program, or a process module. The choice is guided by:
hellowld.c is a hello-world type program. It does nothing particularly useful, but shows the general principle of reading an image, processing it, and writing an output.
%IMDEV%hellowld --help
option -h Writes some text information. Usage: hellowld {options} -h --help write usage data and exit -v --verbose write text information about the processing -i --infile filename(s) for input -o --outfile filename for output -m --multiply one or three comma-separated numbers, to multiply each RGB channel --version write version data and exit
If no --infile is given, the program does nothing. --infile is given a comma-separated list of filenames. If a filename contains a space or comma, it must be quoted. The program reads all the inputs. --multiply is optional, and is given a list of one or three comma-separated floating-point numbers. The RGB channels will be multiplied by these numbers, with each image updated in place. If only one number is supplied, each channel is multiplied by that number. If --outfile is supplied, the image or images are written to that output in the usual ImageMagick way.
%IMDEV%hellowld ^ -i rose: ^ --multiply 0.5,1,1.5 ^ -o sp_rose_mult.jpg |
Note: The IM function MagickReadImage() will accept bad names like "rose:.wizard:" and process as if the argument was simply "rose:". The usual convert program also has this behaviour.
snibconv.c processes no arguments, but passes them all to ConvertImageCommand(). So this program does whatever the convert program does.
%IMDEV%snibconv ^ rose: ^ -resize 400 ^ sp_rose_res.jpg |
Put the line ...
#include <windows.h>
... at the top of the program, and include whatever Microsoft Windows stuff you want.
See the SWIM page.
Call MagickExport MagickBooleanType InvokeDynamicImageFilter(const char *tag, Image **images,const int argc,const char **argv,ExceptionInfo *exception) Check return status: MagickTrue is success.
The program nlightest.c find the (n) lightest pixels in an image, and lists the coordinates, with the lightest first. In addition, after finding each coordinate, it can then ignore all pixels within a certain radius of that coordinate. It writes the coordinates as text to stdout.
We already have a script that does this: nLightest.bat. The script's algorithm is:
The program's algorithm is:
The compiled program does essentially the same processing as the script, but much faster because it doesn't need to store the result of each iteration in a file. The script reads an image twice and writes it once per lightest pixel found, plus an overhead. The compiled program does no IO per lightest pixel found, plus an overhead of one read.
The --help option lists the options:
%IMDEV%nlightest --help
Writes coordinates of (n) lightest pixels to stdout. Usage: nlightest {options} -h --help write usage data and exit -v --verbose write text information about the processing to stderr -i --infile filename for input -o --outfile filename for output -n --numToFind number to find [default 10] -r --radius radius to be ignored; can be suffixed % or c or p --version write version data and exit
%IMDEV%nlightest --version
nlightest v1.0 (c) 2017 Alan Gibson 5-May-2017 ImageMagick 6.9.9-40 Q32 x86_64 2018-05-01 http://www.imagemagick.org Depth Q32 HDRI Compiled by an OpenMP-compliant implementation. OpenMP support.
If no options are given, the program does nothing. To do anything useful, an --infile should be given.
The --numToFind option needs an integer. It limits the number of lightest pixels that will be found. The special value of 0 means there is no limit.
The --radius option gives a distance. No result will be within that distance of any other result. If the radius is 0, results may be adjacent. The number may be suffixed by a character '%' or 'c' or 'p'. '%' and 'c' both mean a percent of the minimum width or height, eg 12.5% or 12.5c. 'p' means a proportion of the minimum width or height, eg 0.125p.
Using "--numToFind 0 --radius 0" will list all the coordinates in the image, starting with the lightest. (If you want that result, a faster method is available: see Process modules: sort pixels blue.)
The --outfile option is not normally useful. When the process completes, the output image is completely black. When the process stops early because --numToFind has been used, the output will be grayscale, with black circles centered on the found coordinates.
We use the script, then the program, to find the ten lightest pixels in toes.png, with an exclusion radius that is 5% of the minimum dimension. We use a timing progam (timec.exe, unpublished) to measure the elapsed times.
First, using the script:
timec %PICTBAT%nLightest toes.png 10 5
255,217 225,45 266,37 93,44 266,141 188,121 264,117 107,40 244,50 253,145 TimeC F:\pictures\nLightest toes.png 10 5 Seconds: 19.69
Now, using the program:
timec %IMDEV%nlightest -itoes.png -n10 -r5c
0.921662 @ 255,217 0.915211 @ 225,45 0.902755 @ 266,37 0.891976 @ 93,44 0.883524 @ 266,141 0.864694 @ 188,121 0.854916 @ 264,117 0.854074 @ 107,40 0.846761 @ 244,50 0.845069 @ 253,145 TimeC C:\cygwin64\home\Alan\imdevins69940\bin\nlightest -itoes.png -n10 -r5c Seconds: 0.84
We get the same result. The compiled program is about 20 times as fast.
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
@rem From image %1, finds (%2) lightest points. @rem @rem After each point is found, @rem blacks out pixels within radius (r) of that point @rem where r = min(width,height) * %3/100 @rem %3 defaults to 10 (percent). @rem @rem Returns number of points found. Could be < n, or even zero (?). @rem @rem Also uses: @rem nlDEBUG if 1, creates debugging image. @rem nlPOINTSIZE optional, pointsize for debug images. @rem nlSTROKEWIDTH for debug. @rem nlONLY_WHITE if 1, doesn't auto-level, @rem so finds only points that are exactly white. @rem @rem Returns: @rem nl_nFound number of points found, integer >= 0 @rem echos list of coordinates. @rem @rem Updated: @rem 24-May-2016 v7 needs -channel RGB for -auto-level. @rem 7-May-2017 "+antialias" the circles. @if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 nl for /F "usebackq" %%L in (`cygpath %TEMP%`) do set CYGTEMP=%%L @set TMPFILE=%TEMP%\%~n1_%sioCODE%.miff @set CYGTMPFILE=%CYGTEMP%\%~n1_%sioCODE%.miff @set LISTFILE=%BASENAME%_%sioCODE%.lis @set TMPDEBUGFILE=%BASENAME%_%sioCODE%_dbg.miff @set DEBUGFILE=%BASENAME%_%sioCODE%_dbg%EXT% @set DEBUGDRAW=%TEMP%\%~n1_%sioCODE%_dbgdrw.txt del %DEBUGDRAW% 2>nul if "%nlPOINTSIZE%"=="" set nlPOINTSIZE=20 if "%nlSTROKEWIDTH%"=="" set nlSTROKEWIDTH=1 set MAX_FIND=%2 if "%MAX_FIND%"=="" set MAX_FIND=10 set PC_RAD=%3 if "%PC_RAD%"=="" set PC_RAD=10 set AUTOLEV=-channel RGB -auto-level +channel if "%nlONLY_WHITE%"=="1" set AUTOLEV= for /F "usebackq" %%L in (`%IM%convert ^ -ping ^ %INFILE% ^ -format "PIX_RAD=%%[fx:int(min(w,h)*%PC_RAD%/100+0.5)]" ^ info:`) do set %%L %IM%convert %INFILE% -colorspace Gray %AUTOLEV% %TMPFILE% if "%nlDEBUG%"=="1" %IM%convert %INFILE% %TMPDEBUGFILE% set /A nFound=0 :loop set MAX= set whiteX= for /F "usebackq tokens=1-3 delims=:, " %%W ^ in (`%IMDEV%convert ^ %TMPFILE% ^ -process onewhite ^ NULL: 2^>^&1`) ^ do ( if "%%W"=="onewhite" ( set MAX=%%W set whiteX=%%X set whiteY=%%Y set /A whiteX2=%%X+%PIX_RAD% ) ) if "!MAX!"=="onewhite" if "!whiteX!" neq "none" ( %IM%convert ^ %TMPFILE% ^ +antialias -fill #000 ^ -draw "circle %whiteX% %whiteY% %whiteX2% %whiteY%" ^ %AUTOLEV% ^ %TMPFILE% if "%nlDEBUG%"=="1" ( ( echo fill None stroke #f00 circle %whiteX%,%whiteY%,%whiteX2%,%whiteY% echo fill #f00 stroke None text %whiteX%,%whiteY% '!nFound!' ) >>%DEBUGDRAW% ) set /A nFound+=1 echo %whiteX%,%whiteY% if !nFound! LSS %MAX_FIND% goto loop ) rem if "%nlDEBUG%"=="1" %IM%convert %TMPDEBUGFILE% %DEBUGFILE% if "%nlDEBUG%"=="1" if exist %DEBUGDRAW% %IM%convert ^ %INFILE% ^ -strokewidth %nlSTROKEWIDTH% ^ -pointsize %nlPOINTSIZE% ^ -draw @%DEBUGDRAW% ^ %DEBUGFILE% call echoRestore @endlocal & set nl_nFound=%nFound%& set nlDEBUG_FILE=%DEBUGFILE%
The following are in the directory %IMSRC%\snibgo:
export PKG_CONFIG_PATH=${IMDEV}../lib/pkgconfig while [ $# -gt 0 ] do cc -o "$1" "$1".c -Imagick -I. -mwindows -Wall -Wa,-march=corei7,-mtune=corei7 `pkg-config --cflags --libs MagickWand` cp "$1".exe ${IMDEV} shift # next option done
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <windows.h> #include <winuser.h> #include "../vsn_defines.h" #if IMV6OR7==6 #include <wand/MagickWand.h> #elif IMV6OR7==7 #include <MagickWand/MagickWand.h> #else #error IMV6OR7 defined but not valid #endif typedef struct { int version; int verbose; char * infile; char * outfile; MagickBooleanType doMult; double multR; double multG; double multB; } ProgOptT; static void testWin (void) { printf ("testWin\n"); HWND hw; printf ("GetDoubleClickTime=%i\n", GetDoubleClickTime ()); //InitializeFlatSB (hw); } static void version (void) { printf ("hellowld v1.0 (c) 2015 Alan Gibson 22-Dec-2015\n"); printf ("%s\n", MagickGetCopyright()); printf ("%s\n", MagickGetVersion(NULL)); exit (0); } static void usage (void) { printf ("Writes some text information.\n\n"); printf ("Usage: hellowld {options}\n"); printf (" -h --help write usage data and exit\n"); printf (" -v --verbose write text information about the processing\n"); printf (" -i --infile filename(s) for input\n"); printf (" -o --outfile filename for output\n"); printf (" -m --multiply one or three comma-separated numbers, to multiply each RGB channel\n"); printf (" --version write version data and exit\n"); exit (0); } static void GetOptions (int argc, char **argv, ProgOptT * po) { struct option long_options[] = { /* Second field is one of: no_argument, required_argument, optional_argument. */ /* Third field is 0 or pointer to int. */ /* These options set a flag. */ {"version", no_argument, &po->version, 1}, {"verbose", no_argument, &po->verbose, 1}, {"brief", no_argument, &po->verbose, 0}, /* These options don't set a flag. We distinguish them by their indices. */ {"help", no_argument, 0, 'h'}, {"add", no_argument, 0, 'a'}, {"append", no_argument, 0, 'b'}, {"create", required_argument, 0, 'c'}, {"delete", required_argument, 0, 'd'}, {"file", required_argument, 0, 'f'}, {"infile", required_argument, 0, 'i'}, {"outfile", required_argument, 0, 'o'}, {"multiply",required_argument, 0, 'm'}, {0, 0, 0, 0} }; char * short_options = "hvabc:d:f:i:o:m:"; MagickBooleanType okay = MagickTrue; po->version = po->verbose = 0; po->infile = po->outfile = NULL; po->doMult = MagickFalse; po->multR = po->multG = po->multB = 0; int c; while (1) { int option_index = 0; c = getopt_long (argc, argv, short_options, long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; // printf ("char [%c]\n", c); switch (c) { case 0: // printf ("long option with option_index `%i'\n", option_index); /* If this option set a flag, do nothing else now. */ if (long_options[option_index].flag != 0) break; // printf ("option %s", long_options[option_index].name); // if (optarg) // printf (" with arg %s", optarg); // printf ("\n"); break; case 'h': puts ("option -h\n"); usage (); break; case 'v': puts ("option -v\n"); po->verbose = 1; break; case 'a': puts ("option -a\n"); break; case 'b': puts ("option -b\n"); break; case 'c': printf ("option -c with value `%s'\n", optarg); break; case 'd': printf ("option -d with value `%s'\n", optarg); break; case 'f': printf ("option -f with value `%s'\n", optarg); break; case 'i': printf ("option -i with value `%s'\n", optarg); po->infile = optarg; // FIXME: is this safe? break; case 'o': printf ("option -o with value `%s'\n", optarg); po->outfile = optarg; break; case 'm': { po->doMult = MagickTrue; printf ("option -m with value `%s'\n", optarg); int n = sscanf (optarg, "%lg,%lg,%lg", &po->multR, &po->multG, &po->multB); if (n!=1 && n!=3) { printf ("--multiply needs 1 or 3 numbers.\n"); okay = MagickFalse; } if (n==1) po->multB = po->multG = po->multR; break; } case '?': /* getopt_long already printed an error message. */ break; default: abort (); } } if (po->version) { version (); } /* Print any remaining command line arguments (not options). */ if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); putchar ('\n'); okay = MagickFalse; } if (!okay) exit (-1); } static void WrOptions (ProgOptT *po) { fprintf (stderr, "Options:\n"); if (po->verbose) fprintf (stderr, " --verbose\n"); if (po->infile) fprintf (stderr, " --infile %s\n", po->infile); if (po->outfile) fprintf (stderr, " --outfile %s\n", po->outfile); if (po->doMult) fprintf (stderr, " --multiply %lg,%lg,%lg\n", po->multR, po->multG, po->multB); } #define ThrowWandException(wand) \ { \ char \ *description; \ \ ExceptionType \ severity; \ \ description=MagickGetException(wand,&severity); \ (void) fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \ description=(char *) MagickRelinquishMemory(description); \ exit(-1); \ } static MagickBooleanType ReadInputs (ProgOptT *po, MagickWand *magick_wand) // po->infile is comma-separated list of filenames. // Each filename may be quoted with single-quote or double-quote characters. { MagickBooleanType status = MagickTrue; const char sq = '\'', dq = '"', cSep = ','; char * p = po->infile; char quote='\0'; // printf ("p:[%s]\n", p); char * pEnd; while (*p) { while (*p==' ') p++; if (*p==sq || *p==dq) { quote = *p; p++; while (*p==' ') p++; pEnd = strchr (p, quote); if (pEnd) { printf ("pEnd:[%s]\n", pEnd); *pEnd = '\0'; pEnd++; } } else { pEnd = p; } char * pSep = strchr (pEnd, cSep); if (pSep) { // printf ("pSep:[%s]\n", pSep); *pSep = '\0'; } if (po->verbose) fprintf (stderr, "reading:[%s]\n", p); status=MagickReadImage (magick_wand, p); if (status == MagickFalse) { ThrowWandException (magick_wand); return status; } p = pSep ? pSep + 1 : strchr (p, '\0'); } return status; } static MagickBooleanType Multiply ( WandView *pixel_view, const ssize_t y,const int id,void *context) { RectangleInfo extent; PIX_INFO pixel; PixelWand **pixels; register long x; ProgOptT *po = context; printf ("%li ", y); extent=GetWandViewExtent(pixel_view); pixels=GetWandViewPixels(pixel_view); for (x=0; x < (long) (extent.width); x++) { PixelGetMagickColor(pixels[x],&pixel); pixel.red *= po->multR; pixel.green *= po->multG; pixel.blue *= po->multB; #if IMV6OR7==6 PixelSetMagickColor(pixels[x],&pixel); #else PixelSetPixelColor(pixels[x],&pixel); #endif } return(MagickTrue); } static void DoOneImage (ProgOptT *po, MagickWand *magick_wand) { MagickBooleanType status; WandView *wand_view; if (po->verbose) fprintf (stderr, "%lix%li\n", MagickGetImageWidth (magick_wand), MagickGetImageHeight (magick_wand) ); if (po->doMult) { // Update the image in-place. wand_view=NewWandView(magick_wand); if (wand_view == (WandView *) NULL) ThrowWandException(magick_wand); status=UpdateWandViewIterator(wand_view,Multiply,(void *) po); if (status == MagickFalse) ThrowWandException(magick_wand); wand_view=DestroyWandView(wand_view); } } int main (int argc, char **argv) { MagickBooleanType status; MagickWand *magick_wand; ProgOptT ProgOpt; GetOptions (argc, argv, &ProgOpt); if (ProgOpt.verbose) { WrOptions (&ProgOpt); } if (ProgOpt.infile) { MagickWandGenesis (); magick_wand=NewMagickWand (); status = ReadInputs (&ProgOpt, magick_wand); if (status == MagickFalse) ThrowWandException (magick_wand); /* Do something with or to each image. */ MagickResetIterator (magick_wand); while (MagickNextImage (magick_wand) != MagickFalse) { DoOneImage (&ProgOpt, magick_wand); } /* Write the image(s) then destroy it/them. */ if (ProgOpt.outfile) { if (ProgOpt.verbose) fprintf (stderr, "Writing [%s]\n", ProgOpt.outfile); status=MagickWriteImages (magick_wand, ProgOpt.outfile, MagickTrue); if (status == MagickFalse) ThrowWandException (magick_wand); } magick_wand=DestroyMagickWand (magick_wand); MagickWandTerminus (); } else { if (ProgOpt.verbose) fprintf (stderr, "Nothing to do."); } testWin (); return(0); }
#include <stdio.h> #include <stdlib.h> #include <wand/MagickWand.h> int main (int argc, char **argv) { MagickBooleanType status; ExceptionInfo * exception = AcquireExceptionInfo (); ImageInfo * image_info = AcquireImageInfo (); status = ConvertImageCommand(image_info, argc, argv, NULL, exception); if (status == MagickFalse) MagickError(exception->severity,exception->reason,exception->description); image_info = DestroyImageInfo (image_info); exception = DestroyExceptionInfo (exception); return (status==MagickFalse) ? 1 : 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include "../vsn_defines.h" #if IMV6OR7==6 #include <magick/MagickCore.h> #elif IMV6OR7==7 #include <MagickCore/MagickCore.h> #else #error IMV6OR7 defined but not valid #endif #ifdef _OPENMP #include <omp.h> #define OPENMP_SUPPORT 1 #endif typedef struct { int version; int verbose; char * infile; char * outfile; int numToFind; char * sRadius; } ProgOptT; static void version (void) { printf ("nlightest v1.0 (c) 2017 Alan Gibson 5-May-2017\n"); printf ("%s\n", GetMagickVersion(NULL)); printf ("Depth Q%i ", MAGICKCORE_QUANTUM_DEPTH); #if defined(MAGICKCORE_HDRI_SUPPORT) printf ("HDRI\n"); #else printf ("integer\n"); #endif #ifdef _OPENMP printf ("Compiled by an OpenMP-compliant implementation.\n"); #else printf ("No OpenMP.\n"); #endif #if defined(OPENMP_SUPPORT) printf ("OpenMP support.\n"); #else printf ("No OpenMP support.\n"); #endif exit (0); } static void usage (void) { printf ("Writes coordinates of (n) lightest pixels to stdout.\n\n"); printf ("Usage: nlightest {options}\n"); printf (" -h --help write usage data and exit\n"); printf (" -v --verbose write text information about the processing to stderr\n"); printf (" -i --infile filename for input\n"); printf (" -o --outfile filename for output\n"); printf (" -n --numToFind number to find [default 10]\n"); printf (" -r --radius radius to be ignored; can be suffixed %% or c or p\n"); printf (" --version write version data and exit\n"); exit (0); } static void WrOptions (ProgOptT *po) { fprintf (stderr, "Options:\n"); if (po->verbose) fprintf (stderr, " --verbose\n"); if (po->infile) fprintf (stderr, " --infile %s\n", po->infile); if (po->outfile) fprintf (stderr, " --outfile %s\n", po->outfile); fprintf (stderr, " --numToFind %i\n", po->numToFind); fprintf (stderr, " --radius %s\n", po->sRadius); } static void GetOptions (int argc, char **argv, ProgOptT * po) // If there is a problem, this doesn't return. { struct option long_options[] = { /* Second field is one of: no_argument, required_argument, optional_argument. */ /* Third field is 0 or pointer to int. */ /* Fourth field is: an integer, if third is a pointer to int; a character index, otherwise. */ /* These options set a flag. */ {"version", no_argument, &po->version, 1}, {"verbose", no_argument, &po->verbose, 1}, {"brief", no_argument, &po->verbose, 0}, /* These options don't set a flag. We distinguish them by their indices. */ {"help", no_argument, 0, 'h'}, {"infile", required_argument, 0, 'i'}, {"outfile", required_argument, 0, 'o'}, {"numToFind", required_argument, 0, 'n'}, {"radius", required_argument, 0, 'r'}, {0, 0, 0, 0} }; char * short_options = "hvi:o:n:r:"; MagickBooleanType okay = MagickTrue; po->version = po->verbose = 0; po->infile = po->outfile = NULL; po->numToFind = 10; po->sRadius = ""; int c; while (1) { int option_index = 0; c = getopt_long (argc, argv, short_options, long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; // printf ("char [%c]\n", c); switch (c) { case 0: // printf ("long option with option_index `%i'\n", option_index); /* If this option sets a flag, do nothing else now. */ if (long_options[option_index].flag != 0) break; //printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case 'h': usage (); break; case 'v': po->verbose = 1; break; case 'i': po->infile = optarg; break; case 'o': po->outfile = optarg; break; case 'n': po->numToFind = atoi(optarg); break; case 'r': po->sRadius = optarg; break; case '?': /* getopt_long already printed an error message. */ break; default: abort (); } } if (po->version) { version (); } /* Print any remaining command line arguments (not options). */ if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); putchar ('\n'); okay = MagickFalse; } if (po->verbose) { WrOptions (po); } if (!okay) exit (-1); } // ThrowWandException() doesn't return. #define ThrowWandException(wand) \ { \ char \ *description; \ \ ExceptionType \ severity; \ \ description=MagickGetException(wand,&severity); \ fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \ description=(char *) MagickRelinquishMemory(description); \ exit(-1); \ } #define snibw_threads(source,destination,chunk,expression) \ num_threads((expression) == 0 ? 1 : \ (((chunk) > (32*GetMagickResourceLimit(ThreadResource))) && \ (GetImagePixelCacheType(source) != DiskCache)) && \ (GetImagePixelCacheType(destination) != DiskCache) ? \ GetMagickResourceLimit(ThreadResource) : \ GetMagickResourceLimit(ThreadResource) < 2 ? 1 : 2) #define snibw_threadsXX(source,destination,chunk,expression) \ num_threads(GetMagickResourceLimit(ThreadResource)) typedef struct { ssize_t xAtMax, yAtMax; Quantum maxVal; } ThreadT; static MagickBooleanType FindLightest (ProgOptT *po) { if (!po->infile) return MagickFalse; ExceptionInfo *exception = AcquireExceptionInfo (); ImageInfo *image_info = CloneImageInfo((ImageInfo *) NULL); (void) strcpy(image_info->filename,po->infile); Image * image = ReadImage(image_info,exception); if (!image) return MagickFalse; if (po->verbose) printf ("ThreadResource %llu\n", GetMagickResourceLimit(ThreadResource)); if (!TransformImageColorspace(image, GRAYColorspace)) return MagickFalse; if (po->verbose) fprintf (stderr, "numToFind=%i\n", po->numToFind); ssize_t radius = 0; if (po->sRadius) { ssize_t minDim = image->columns; if (minDim > image->rows) { minDim = image->rows; } char * chLast; double val = InterpretLocaleValue (po->sRadius, &chLast); if (*chLast == '%') *chLast = 'c'; if (*chLast == 'c') { radius = floor (val * minDim / 100.0 + 0.5); } else if (*chLast == 'p') { radius = floor (val * minDim + 0.5); } else { radius = floor (val + 0.5); } if (po->verbose) fprintf (stderr, "val=%g radius=%li\n", val, radius); } if (po->verbose) fprintf (stderr, "radius=%li\n", radius); DrawInfo *draw_info = AcquireDrawInfo(); // Default fill colour is black. ssize_t y; MagickBooleanType okay = MagickTrue; int nFound = 0; int maxThr = GetMagickResourceLimit(ThreadResource); if (po->verbose) printf ("maxThr=%i\n", maxThr); ThreadT * threads = (ThreadT *)malloc (maxThr * sizeof(ThreadT)); CacheView *image_view = AcquireVirtualCacheView (image,exception);; do { int i; for (i = 0; i < maxThr; i++) { threads[i].xAtMax = 0; threads[i].yAtMax = 0; threads[i].maxVal = 0; } #if defined(OPENMP_SUPPORT) #pragma omp parallel for \ schedule(static) \ snibw_threads ( \ image, \ image, \ image->rows,1) #endif for (y=0; y < (ssize_t) image->rows; y++) { int thrNum = omp_get_thread_num(); const VIEW_PIX_PTR *p = GetCacheViewVirtualPixels ( image_view,0,y,image->columns,1,exception); if (!p) { okay = MagickFalse; continue; } ThreadT * pt = &threads[thrNum]; ssize_t x; for (x=0; x < image->columns; x++) { double r = GET_PIXEL_RED (image, p); if (pt->maxVal < r) { pt->maxVal = r; pt->xAtMax = x; pt->yAtMax = y; //printf (" y=%li %g %li,%li\n", y, r, x, y); } p += Inc_ViewPixPtr (image); } } // end y loop if (!okay) break; ssize_t yAtMax=0, xAtMax=0; Quantum maxVal = 0; for (i = 0; i < maxThr; i++) { // printf ("thr %i: %g @ %li,%li\n", i, threads[i].maxVal, threads[i].xAtMax, threads[i].yAtMax); if (maxVal < threads[i].maxVal) { maxVal = threads[i].maxVal; xAtMax = threads[i].xAtMax; yAtMax = threads[i].yAtMax; } } if (maxVal==0) break; if (po->verbose) fprintf (stderr, "max val=%g @ %li,%li\n", maxVal, xAtMax, yAtMax); printf ("%g @ %li,%li\n", maxVal/(double)QuantumRange, xAtMax, yAtMax); char primitive[MaxTextExtent]; if (radius < 1) { FormatLocaleString(primitive,MaxTextExtent, "point %li,%li", xAtMax, yAtMax); } else { FormatLocaleString(primitive,MaxTextExtent, "circle %li,%li %li,%li", xAtMax, yAtMax, xAtMax, (long int)(yAtMax+radius+0.5)); } CloneString(&draw_info->primitive,primitive); #if IMV6OR7==6 if (!DrawImage (image, draw_info)) break; #else if (!DrawImage (image, draw_info, exception)) break; #endif nFound++; } while (po->numToFind==0 || nFound < po->numToFind); if (po->verbose) fprintf (stderr, "Number found: %i\n", nFound); free (threads); if (po->outfile) { strcpy (image->filename, po->outfile); if (!WriteImage (image_info, image)) { fprintf (stderr, "WriteImage [%s] failed.\n", po->outfile); } } image_view = DestroyCacheView (image_view); image = DestroyImage (image); image_info = DestroyImageInfo (image_info); exception = DestroyExceptionInfo (exception); return MagickTrue; } int main (int argc,char **argv) { int ret = 0; ProgOptT ProgOpt; MagickCoreGenesis (*argv, MagickTrue); GetOptions (argc, argv, &ProgOpt); if (!FindLightest (&ProgOpt)) { fprintf (stderr, "FindLightest failure\n"); ret = 1; } MagickCoreTerminus (); return (ret); }
All images on this page were created by the commands shown, using:
%IM%identify -version
Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC License: http://www.imagemagick.org/script/license.php Visual C++: 180040629 Features: Cipher DPC Modules OpenMP Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib
%IMDEV%identify -version
Version: ImageMagick 6.9.9-40 Q32 x86_64 2018-05-01 http://www.imagemagick.org Copyright: © 1999-2018 ImageMagick Studio LLC License: http://www.imagemagick.org/script/license.php Features: Cipher DPC HDRI Modules OpenMP Delegates (built-in): bzlib cairo fftw fontconfig fpx freetype jbig jng jpeg lcms ltdl lzma pangocairo png rsvg tiff webp wmf x xml zlib
To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG to JPG.
Source file for this web page is stanprog.h1. To re-create this web page, run "procH1 stanprog".
This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.
Anyone is permitted to re-publish this page, but only for non-commercial use.
Anyone is permitted to link to this page, including for commercial use.
Page version v1.0 23-March-2015.
Page created 25-Aug-2018 02:03:22.
Copyright © 2018 Alan Gibson.