We experiment with RMS thresholds that classify image pairs as "same" or "different".
This page continues testing from Perceptual hash tests. (For references, see that page.) That page took a small number of standard images, created many variations ("attacks") of each, and calculated the phash of every image, in combinations of up to four colorspaces. For every colorspace combination, it tested RMSE phash diferences between every pair of images. Two measures of "goodness" were used, according to how many images were closest to an image that wasn't derived from the same standard image, or how many were closest to a standard image that wasn't the actual source for the variation.
On this page, we use measures of false positive and false negative matches at different RMS thresholds to find the "best" threshold for each colorspace combination. The measures are: sum of false rates, and Matthews correlation coefficient. These measures also indicate the "goodness" of each colorspace combination, so we get a ranking order of the combinations.
The pages have different criteria for goodness, so generate different ranking orders.
The page Perceptual hash tests has calculated phash values for every image, in a large number of colorspace combinations. From these numbers, we can calculate RMS differences between any pair of images; this number is zero when the images are identical, and larger numbers mean the images are more different. At each combination, it has found the lowest RMS difference between images that were variations of different standard images (loNonMat), and the largest RMS difference between images that were variations of the same standard images (hiMat).
For this image set, loNonMat < hiMat.
If we choose a threshold less than loNonMat, all the images from different sources will score above the threshold so will correctly be considered different; there will be no false positives. However, there will be many false negatives.
If we choose a threshold greater than hiMat, all the images from the same source will score below the threshold so will correctly be considered the same; there will be no false negatives. However, there will be many false positives.
If we choose a threshold between those limits, we get false positives and false negatives.
The script setPhEnv.bat sets up the environment:
call %PICTBAT%setPhEnv
This page needs Inkscape:
call %PICTBAT%setInkPath
We want the "optimum" threshold for the RMS phash. But what defines optimum? What do we want to optimise?
References:
Of all the possible pairs, we know which ones should match (eg every Lena variation should match every other Lena variation), and which ones shouldn't (eg a Lena variation shouldn't match a Barbara variation).
If the RMS difference betwen the pair is below the given threshold, we call it a positive match, and we also know if this is a true positive or false positive. Similarly when the RMS difference is above the threshold, we call it a negative match, we know if this is a true negative or false negative.
With this dataset, no threshold gives zero false positives and zero false negatives. We can only tradeoff one against the other.
We will draw Detection Error Tradeoff (DET) curves. This will map the false negative rate against the false positive rate, for various values of RMS threshold.
gensvg /itrueFalsePosNeg.s1 /P
If we have R records, the number of pairs is nPairs = R*(R-1)/2. In the diagram, the total rectangle represents nPairs.
Of these nPairs pairs, actualPos pairs will match and actualNeg = (nPairs - actualPos) will not match. In the diagram, the green shape represents actualPos, and everything outside the green shape represents actualNeg.
Each test run will consider p pairs to match and q = (nPairs - p) pairs to not match. In the diagram, the red shape represents p.
If the method was perfect for the dataset, the red shape would coincide with the green shape, so falseNeg and falsePos would both be zero.
Of the p pairs that are considered to match, truePos are true matches and falsePos = (p - truePos) are false matches.
Of the q pairs that are considered to not match, trueNeg are true non-matches and falseNeg = (q - trueNeg) are false non-matches.
Hence:
truePos + falseNeg = actualPos trueNeg + falsePos = actualNeg
Instead of using integer counts of pairs, we divide to make false positive and false negative rates that range from 0.0 (a perfect score) to 1.0 (as bad as it could be.)
FPrate = falsePos / actualNeg = falsePos / (trueNeg + falsePos) FNrate = falseNeg / actualPos = falseNeg / (truePos + falseNeg)
We will plot the FNrate (y-axis) against FPrate (x-axis) for a number of thresholds.
call %PICTBAT%phpairs 21_HSB call %PICTBAT%phpairs 21_YDbDr call %PICTBAT%phpairs 42_HSB_YDbDr call %PICTBAT%phpairs 42_HSB_xyY
At any FPrate, the YDbDr colorspace gives a better (nearer zero) FNrate than the HSB colorspace. And the combination of two colorspaces HSB+YDrBr is better still. Where many points are plotted close together, this is where scorePairsNPD has searched for a minimum of (FPrate + FNrate).
We can plot all the single-colorspaces on one graph, zooming in to a small range.
Okay, but what is the best threshold value? I'm not a statistician. A naive "best" threshold is that which minimizes FPFN = (FPrate + FNrate). Howevever, perhaps the threshold that maximizes the Matthews Correlation Coefficient is more appropriate.
MCC values range from +1 through 0 to -1. MCC = +1 means the method was entirely correct. MCC = 0 means the method was no better than random guesses. MCC = -1 means the method gave entirely the wrong answers. As I prefer to minimize rather than maximize, I use MCCzero = (1 - MCC).
MCC is calculated by:
n = truePos * trueNeg - falsePos * falseNeg d = (truePos + falsePos) * (truePos + falseNeg) * (trueNeg + falsePos) * (trueNeg + falseNeg) if d==0 then d = 1 MCC = n / sqrt (d)
This provides alternate measures for the best colorspace combination: it is the colorspace that yields the lowest FPFN or MCCzero.
The program scorePairsMaxMcc.c iterates to find the threshold, for one colorspace combination, that minimizes MCCzero. This takes around six seconds (single-threaded). (The program would be faster if we stored the RMS differences of the 6,859,841,200 comparisons.)
The script phpPairsBestAll.bat repeats this for all 31,930 combinations of colorspace, which takes around 53 hours.
We list the results in decreasing order of MCC, so the colorspace combinations at the top are the best.
call %PICTBAT%phpPairsBestAll phpbest_XX.csv if ERRORLEVEL 1 goto error cHead /iphpbest_2.csv /ophpbest_2_15.csv /h16 cHead /iphpbest_3.csv /ophpbest_3_15.csv /h16 cHead /iphpbest_4.csv /ophpbest_4_15.csv /h16 call csv2tab phpbest_1 call csv2tab phpbest_2_15 call csv2tab phpbest_3_15 call csv2tab phpbest_4_15
The results for all of the single colorspaces:
threshold | truePos | trueNeg | falsePos | falseNeg | FPrate | FNrate | FPFN | MCC | colorspace |
---|---|---|---|---|---|---|---|---|---|
0.566716 | 34792 | 374204 | 2340 | 18344 | 0.00621441 | 0.345227 | 0.351442 | 0.759855 | OHTA |
0.539183 | 33320 | 373910 | 2634 | 19816 | 0.0069952 | 0.37293 | 0.379925 | 0.737184 | Luv |
0.51604 | 32422 | 374268 | 2276 | 20714 | 0.00604445 | 0.38983 | 0.395874 | 0.729943 | Lab |
0.3807 | 31546 | 375068 | 1476 | 21590 | 0.00391986 | 0.406316 | 0.410236 | 0.728905 | CbCr |
0.3746 | 31976 | 374272 | 2272 | 21160 | 0.00603382 | 0.398223 | 0.404257 | 0.724115 | UV |
0.308234 | 31404 | 374720 | 1824 | 21732 | 0.00484406 | 0.408988 | 0.413832 | 0.722398 | CC |
0.434004 | 30778 | 375312 | 1232 | 22358 | 0.00327186 | 0.420769 | 0.424041 | 0.722087 | YCC |
0.745734 | 32638 | 373300 | 3244 | 20498 | 0.0086152 | 0.385765 | 0.39438 | 0.720655 | HCL |
0.537023 | 34294 | 371232 | 5312 | 18842 | 0.0141072 | 0.3546 | 0.368707 | 0.718422 | YCbCr |
0.442501 | 30096 | 375084 | 1460 | 23040 | 0.00387737 | 0.433604 | 0.437482 | 0.709886 | YUV |
0.649168 | 32152 | 372446 | 4098 | 20984 | 0.0108832 | 0.394911 | 0.405794 | 0.703803 | xyY |
0.506286 | 31444 | 373102 | 3442 | 21692 | 0.00914103 | 0.408235 | 0.417377 | 0.70223 | YIQ |
0.682285 | 32982 | 370770 | 5774 | 20154 | 0.0153342 | 0.379291 | 0.394625 | 0.695683 | RGB |
0.731083 | 38414 | 362992 | 13552 | 14722 | 0.0359905 | 0.277063 | 0.313053 | 0.69356 | YDbDr |
0.710234 | 29800 | 374114 | 2430 | 23336 | 0.00645343 | 0.439175 | 0.445628 | 0.692838 | HSB |
0.520259 | 28254 | 375060 | 1484 | 24882 | 0.00394111 | 0.46827 | 0.472211 | 0.684555 | sRGB |
0.326969 | 27624 | 375218 | 1326 | 25512 | 0.0035215 | 0.480126 | 0.483648 | 0.678107 | IQ |
0.91192 | 36580 | 362440 | 14104 | 16556 | 0.0374564 | 0.311578 | 0.349034 | 0.664366 | HSL |
0.609697 | 29732 | 371926 | 4618 | 23404 | 0.0122642 | 0.440455 | 0.452719 | 0.664308 | CL |
0.534095 | 27516 | 373586 | 2958 | 25620 | 0.00785566 | 0.482159 | 0.490015 | 0.654029 | LMS |
0.47499 | 26044 | 374788 | 1756 | 27092 | 0.00466347 | 0.509861 | 0.514525 | 0.649679 | XYZ |
0.744342 | 30400 | 369742 | 6802 | 22736 | 0.0180643 | 0.427883 | 0.445947 | 0.648578 | HSI |
0.604548 | 28534 | 371288 | 5256 | 24602 | 0.0139585 | 0.463001 | 0.476959 | 0.639671 | SB |
0.437131 | 25010 | 374998 | 1546 | 28126 | 0.00410576 | 0.529321 | 0.533427 | 0.637854 | DbDr |
0.527649 | 27114 | 372250 | 4294 | 26022 | 0.0114037 | 0.489724 | 0.501128 | 0.63093 | LCHuv |
0.484239 | 25236 | 374190 | 2354 | 27900 | 0.00625159 | 0.525068 | 0.531319 | 0.629422 | LCH |
0.568024 | 25670 | 371666 | 4878 | 27466 | 0.0129547 | 0.5169 | 0.529855 | 0.602261 | SL |
0.465849 | 21064 | 374102 | 2442 | 32072 | 0.0064853 | 0.603583 | 0.610069 | 0.564476 | SI |
0.574696 | 20026 | 372266 | 4278 | 33110 | 0.0113612 | 0.623118 | 0.634479 | 0.52089 | HWB |
0.5075 | 19964 | 372306 | 4238 | 33172 | 0.011255 | 0.624285 | 0.63554 | 0.520407 | WB |
The combinations of two colorspaces, showing just the top 15 out of 435:
threshold | truePos | trueNeg | falsePos | falseNeg | FPrate | FNrate | FPFN | MCC | colorspace |
---|---|---|---|---|---|---|---|---|---|
0.594606 | 37588 | 374836 | 1708 | 15548 | 0.00453599 | 0.292608 | 0.297144 | 0.802692 | OHTA+UV |
0.572529 | 36560 | 375760 | 784 | 16576 | 0.00208209 | 0.311954 | 0.314036 | 0.801611 | CbCr+OHTA |
0.622898 | 37092 | 374514 | 2030 | 16044 | 0.00539114 | 0.301942 | 0.307333 | 0.792635 | Luv+OHTA |
0.550261 | 36256 | 375134 | 1410 | 16880 | 0.00374458 | 0.317675 | 0.32142 | 0.78991 | CC+OHTA |
0.58455 | 36112 | 375204 | 1340 | 17024 | 0.00355868 | 0.320385 | 0.323944 | 0.789002 | OHTA+YUV |
0.575954 | 35646 | 375366 | 1178 | 17490 | 0.00312845 | 0.329155 | 0.332284 | 0.785257 | OHTA+YCbCr |
0.671733 | 36488 | 374482 | 2062 | 16648 | 0.00547612 | 0.313309 | 0.318785 | 0.784718 | DbDr+OHTA |
0.887957 | 37114 | 373744 | 2800 | 16022 | 0.00743605 | 0.301528 | 0.308964 | 0.783679 | HSB+xyY |
0.887873 | 36692 | 373812 | 2732 | 16444 | 0.00725546 | 0.30947 | 0.316725 | 0.779188 | HSI+xyY |
0.849611 | 38214 | 372076 | 4468 | 14922 | 0.0118658 | 0.280827 | 0.292692 | 0.778456 | HCL+IQ |
0.568498 | 35752 | 374494 | 2050 | 17384 | 0.00544425 | 0.32716 | 0.332605 | 0.775626 | OHTA+YCC |
0.55559 | 35620 | 374622 | 1922 | 17516 | 0.00510432 | 0.329645 | 0.334749 | 0.77555 | CbCr+Luv |
0.589098 | 35708 | 374460 | 2084 | 17428 | 0.00553455 | 0.327989 | 0.333523 | 0.774652 | Lab+OHTA |
0.549354 | 34602 | 375468 | 1076 | 18534 | 0.00285757 | 0.348803 | 0.351661 | 0.773491 | UV+YIQ |
0.549354 | 34602 | 375468 | 1076 | 18534 | 0.00285757 | 0.348803 | 0.351661 | 0.773491 | IQ+YUV |
The combinations of three colorspaces, showing just the top 15 out of 4060:
threshold | truePos | trueNeg | falsePos | falseNeg | FPrate | FNrate | FPFN | MCC | colorspace |
---|---|---|---|---|---|---|---|---|---|
0.893094 | 39096 | 374486 | 2058 | 14040 | 0.0054655 | 0.264228 | 0.269693 | 0.816943 | HSB+IQ+xyY |
0.886205 | 39644 | 373632 | 2912 | 13492 | 0.00773349 | 0.253914 | 0.261648 | 0.813693 | HCL+IQ+xyY |
0.887346 | 38662 | 374394 | 2150 | 14474 | 0.00570982 | 0.272395 | 0.278105 | 0.810553 | HSI+IQ+xyY |
0.882682 | 39052 | 373938 | 2606 | 14084 | 0.00692084 | 0.265056 | 0.271977 | 0.809973 | HSB+xyY+YIQ |
0.868723 | 38546 | 374432 | 2112 | 14590 | 0.00560891 | 0.274578 | 0.280187 | 0.809584 | CbCr+HSB+xyY |
0.575133 | 37286 | 375618 | 926 | 15850 | 0.00245921 | 0.298291 | 0.30075 | 0.808698 | CbCr+OHTA+UV |
0.864258 | 38302 | 374556 | 1988 | 14834 | 0.0052796 | 0.27917 | 0.28445 | 0.808074 | HSB+UV+xyY |
0.612466 | 37738 | 375120 | 1424 | 15398 | 0.00378176 | 0.289785 | 0.293566 | 0.808017 | CbCr+Luv+OHTA |
0.894512 | 39062 | 373754 | 2790 | 14074 | 0.00740949 | 0.264868 | 0.272277 | 0.807962 | DbDr+HSB+xyY |
0.860773 | 38012 | 374644 | 1900 | 15124 | 0.00504589 | 0.284628 | 0.289674 | 0.805573 | HSB+OHTA+xyY |
0.614159 | 37766 | 374872 | 1672 | 15370 | 0.00444038 | 0.289258 | 0.293698 | 0.805319 | Luv+OHTA+UV |
0.899114 | 38660 | 373926 | 2618 | 14476 | 0.00695271 | 0.272433 | 0.279386 | 0.805017 | HSL+IQ+xyY |
0.864075 | 38608 | 373948 | 2596 | 14528 | 0.00689428 | 0.273412 | 0.280306 | 0.804634 | HSB+Lab+xyY |
0.555426 | 37002 | 375520 | 1024 | 16134 | 0.00271947 | 0.303636 | 0.306355 | 0.803985 | CC+OHTA+UV |
0.546537 | 36606 | 375886 | 658 | 16530 | 0.00174747 | 0.311089 | 0.312836 | 0.803794 | CbCr+CC+OHTA |
The combinations of four colorspaces, showing just the top 15 out of 27,405:
threshold | truePos | trueNeg | falsePos | falseNeg | FPrate | FNrate | FPFN | MCC | colorspace |
---|---|---|---|---|---|---|---|---|---|
0.886623 | 40760 | 373874 | 2670 | 12376 | 0.0070908 | 0.232912 | 0.240003 | 0.830014 | CbCr+HSB+IQ+xyY |
0.898802 | 40660 | 373856 | 2688 | 12476 | 0.00713861 | 0.234794 | 0.241932 | 0.828601 | DbDr+HSB+IQ+xyY |
0.882317 | 40502 | 373942 | 2602 | 12634 | 0.00691022 | 0.237767 | 0.244677 | 0.827672 | HSB+IQ+UV+xyY |
0.868172 | 39570 | 374552 | 1992 | 13566 | 0.00529022 | 0.255307 | 0.260597 | 0.82348 | HSB+IQ+OHTA+xyY |
0.859046 | 39474 | 374550 | 1994 | 13662 | 0.00529553 | 0.257114 | 0.262409 | 0.822293 | CbCr+HSB+OHTA+xyY |
0.876247 | 39804 | 374192 | 2352 | 13332 | 0.00624628 | 0.250903 | 0.25715 | 0.8221 | DbDr+HCL+IQ+xyY |
0.861948 | 38976 | 374998 | 1546 | 14160 | 0.00410576 | 0.266486 | 0.270592 | 0.821608 | DbDr+HSB+OHTA+xyY |
0.858647 | 39568 | 374366 | 2178 | 13568 | 0.00578418 | 0.255345 | 0.261129 | 0.821267 | HSB+IQ+Lab+xyY |
0.854104 | 39230 | 374706 | 1838 | 13906 | 0.00488124 | 0.261706 | 0.266587 | 0.821185 | HSB+OHTA+UV+xyY |
0.86422 | 39250 | 374652 | 1892 | 13886 | 0.00502465 | 0.261329 | 0.266354 | 0.820785 | HSI+IQ+OHTA+xyY |
0.856227 | 39418 | 374412 | 2132 | 13718 | 0.00566202 | 0.258168 | 0.26383 | 0.819986 | HSB+IQ+xyY+YCbCr |
0.856227 | 39418 | 374412 | 2132 | 13718 | 0.00566202 | 0.258168 | 0.26383 | 0.819986 | CbCr+HSB+xyY+YIQ |
0.86812 | 39978 | 373814 | 2730 | 13158 | 0.00725015 | 0.247629 | 0.254879 | 0.819851 | CC+HSB+IQ+xyY |
0.85785 | 39058 | 374760 | 1784 | 14078 | 0.00473783 | 0.264943 | 0.269681 | 0.819742 | CbCr+DbDr+HSB+xyY |
0.870442 | 39666 | 374108 | 2436 | 13470 | 0.00646936 | 0.2535 | 0.25997 | 0.819447 | CbCr+HSI+IQ+xyY |
In the lists above, what colorspaces occur most frequently?
call %PICTBAT%phUnionTh phh_popcolsp.csv call csv2tabNH phh_popcolsp
Colorspace | Count |
OHTA | 16 |
xyY | 13 |
HSB | 9 |
IQ | 7 |
UV | 7 |
CbCr | 7 |
Luv | 5 |
CC | 4 |
HSI | 3 |
Lab | 3 |
DbDr | 3 |
YUV | 3 |
HCL | 3 |
YIQ | 3 |
YCbCr | 2 |
HSL | 2 |
YCC | 2 |
sRGB | 1 |
YDbDr | 1 |
LMS | 1 |
XYZ | 1 |
WB | 1 |
LCHuv | 1 |
SL | 1 |
SI | 1 |
SB | 1 |
RGB | 1 |
LCH | 1 |
HWB | 1 |
CL | 1 |
The method on Perceptual hash tests page measures whether the closest match to a given image is a correct one, and doesn't care about whether more distant matches are also correct.
By contrast, this page measures the goodness of colorspace combination by a calculation from true or false positives or negatives, given a certain RMS threshold. This measure is appropriate when the application is to find images that match and reject those that don't.
My recommendation for IM internals are the same as shown on Perceptual hash tests, except that the recommended colorspaces, when two are used, was given there as HSB+xyY. On ths page, we find that HSB+xyY has an MCC score of 0.783679. The best MCC score, of 0.802692, comes from OHTA+UV. This is not a large difference; when the test is MCC of false positives and negatives, using two colorspaces, the test is not highly sensitive to the choice of colorspaces.
In this test, HSB and xyY appear among the top three colorspaces in the "frequently occur" list of best results. The poll positon is taken by OHTA, which scored notably badly on the Perceptual hash tests page.
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
rem Set environment for perceptual hash tests. rem --------------------------------------- rem Directories. Names must NOT contain underscore. set DATASET1_DIR=\web\im\testImages set PH_TWEAKED=phtweaked if not exist %PH_TWEAKED% md %PH_TWEAKED% set PH_CSVDIR=phcsv if not exist %PH_CSVDIR% md %PH_CSVDIR% rem --------------------------------------- rem Colorspaces rem Note: rem CL and CLp would give the same result. rem YCbCr and YPbPr would give the same result. rem RGB and scRGB would give the same result. set COLSP3=sRGB RGB HCL HSB HSI HSL HWB Lab Luv LCH LCHuv LMS OHTA xyY XYZ YCbCr YCC YDbDr YIQ YUV rem Next omits channel 0, which is "H". rem Note: rem SB and SV would give the same result. rem CL and CLp would give the same result. set COLSP_NO_H=CL SB SI SL WB set COLSP_NO_Y=CbCr CC DbDr IQ UV set COLSPACES=%COLSP3% %COLSP_NO_H% %COLSP_NO_Y% rem Use all colorspaces: set COLSP_FOR3=%COLSPACES%
@rem @rem Updated: @rem 15-August-2022 for IM v7. @rem rem %1 is colorspace combination eg 63_HSB_IQ_xyY call %PICTBAT%setPhEnv set COLSP_COMB=%1 set TITLE=%COLSP_COMB:_=+% set TITLE=%TITLE:21+=% set TITLE=%TITLE:42+=% set TITLE=%TITLE:63+=% set TITLE=%TITLE:84+=% set COLSP_CSV=%PH_CSVDIR%\idPH%COLSP_COMB%.csv set TMP_RESULTS=%TEMP%\php.csv call %PICTBAT%phpThreshCsv %COLSP_CSV% %TMP_RESULTS% if ERRORLEVEL 1 exit /B 1 set PLOT=php_%COLSP_COMB% gnuplot-base ^ -c %PICTBAT%plotScr.gp ^ %TMP_RESULTS% %PLOT%.svg 500 500 "2" ^ "FPrate" "FNrate" ^ "set key inside top right noautotitle title '%TITLE%';set xrange [0:1];set yrange [0:1];set size square" rem "set key inside top right noautotitle title '%TITLE%';set logscale;set xrange [0.001:1];set yrange [0.001:1];set size square" if ERRORLEVEL 1 exit /B 1 rem set yrange [0:1]; %IMG7%magick ^ -background None %PLOT%.svg ^ -trim +repage ^ -bordercolor None -border 5 ^ -background #eee -layers flatten ^ %PLOT%.png
rem From %1, a CSV of PH values for images, rem makes %2 a CSV with varying thresholds of RMS. setlocal set COLSP_CSV=%1 set OUTFILE=%2 set TMP_RESULTS=%TEMP%\ptc.csv if not exist %COLSP_CSV% ( echo Can't find %COLSP_CSV% exit /B 1 ) scorePairsNPD %COLSP_CSV% >%TMP_RESULTS% cGrep /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /sFPFN chStrs /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /f", " /t"," chStrs /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /f" " /t"," call deEqCols %TMP_RESULTS% %OUTFILE% if ERRORLEVEL 1 exit /B 1 cProject /p0 /i%OUTFILE% /o%OUTFILE% /h /kFPrate,FNrate,FPFN,MCC,threshold if ERRORLEVEL 1 exit /B 1 cSort /p0 /i%OUTFILE% /o%OUTFILE% /h /kFPrate type %TMP_RESULTS% endlocal
rem From %1, a CSV of PH values for images, rem makes %2 a CSV with best thresholds of RMS, rem with colorspace column value %3. setlocal set COLSP_CSV=%1 set OUTFILE=%2 set TMP_RESULTS=%TEMP%\ptc.csv if not exist %COLSP_CSV% ( echo Can't find %COLSP_CSV% exit /B 1 ) scorePairsMaxMcc %COLSP_CSV% >%TMP_RESULTS% cGrep /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /sbest /t\0 cGrep /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /sFPFN chStrs /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /f", " /t"," chStrs /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /f" " /t"," cPrefix /p0 /i%TMP_RESULTS% /o%TMP_RESULTS% /r",colorspace=%3" call deEqCols %TMP_RESULTS% %OUTFILE% if ERRORLEVEL 1 exit /B 1 type %OUTFILE% endlocal
rem Creates output CSV files with best results. rem %1 is name format, must contain XX. rem XX will be replaced with 1, 2, 3 or 4. setlocal call %PICTBAT%setPhEnv set OUT_FMT=%1 set USE_COLS=threshold,truePos,trueNeg,falsePos,falseNeg,FPrate,FNrate,FPFN,MCC,colorspace rem ------------------------------------------------ rem One colorspace. goto skip1 set OUTFILE=%OUT_FMT:XX=1% del phbest21*.csv 2>nul for %%C in (%COLSPACES%) do ( call %PICTBAT%phCombin %%C if ERRORLEVEL 1 exit /B 1 set INCSV=%PH_CSVDIR%\idPh!PH_COMBIN!.csv set OUTCSV=phbest!PH_COMBIN!.csv call %PICTBAT%phpThreshCsvBest !INCSV! !OUTCSV! !PH_PLUS! if ERRORLEVEL 1 exit /B 1 ) cat phbest21*.csv >%OUTFILE% cSort /p0 /i%OUTFILE% /o%OUTFILE% /u /r cProject /p0 /i%OUTFILE% /o%OUTFILE% /h /k%USE_COLS% cSort /p0 /i%OUTFILE% /o%OUTFILE% /h /kMCC /r type %OUTFILE% :skip1 rem ------------------------------------------------ rem Combinations of two colorspaces. goto skip2 set OUTFILE=%OUT_FMT:XX=2% del phbest42*.csv 2>nul for %%C in (%COLSPACES%) do ( for %%D in (%COLSPACES%) do ( if /I %%C LSS %%D ( call %PICTBAT%phCombin %%C %%D if ERRORLEVEL 1 exit /B 1 set INCSV=%PH_CSVDIR%\idPh!PH_COMBIN!.csv set OUTCSV=phbest!PH_COMBIN!.csv call %PICTBAT%phpThreshCsvBest !INCSV! !OUTCSV! !PH_PLUS! if ERRORLEVEL 1 exit /B 1 ) ) ) cat phbest42*.csv >%OUTFILE% cSort /p0 /i%OUTFILE% /o%OUTFILE% /u /r cSort /p0 /i%OUTFILE% /o%OUTFILE% /h /kMCCzero cProject /p0 /i%OUTFILE% /o%OUTFILE% /h /k%USE_COLS% type %OUTFILE% :skip2 rem ------------------------------------------------ rem Combinations of three colorspaces. goto skip3 set OUTFILE=%OUT_FMT:XX=3% del phbest63*.csv 2>nul for %%C in (%COLSPACES%) do ( for %%D in (%COLSPACES%) do ( if /I %%C LSS %%D ( for %%E in (%COLSPACES%) do ( if /I %%D LSS %%E ( call %PICTBAT%phCombin %%C %%D %%E if ERRORLEVEL 1 exit /B 1 set INCSV=%PH_CSVDIR%\idPh!PH_COMBIN!.csv set OUTCSV=phbest!PH_COMBIN!.csv call %PICTBAT%phpThreshCsvBest !INCSV! !OUTCSV! !PH_PLUS! if ERRORLEVEL 1 exit /B 1 ) ) ) ) ) cat phbest63*.csv >%OUTFILE% cSort /p0 /i%OUTFILE% /o%OUTFILE% /u /r cSort /p0 /i%OUTFILE% /o%OUTFILE% /h /kMCCzero cProject /p0 /i%OUTFILE% /o%OUTFILE% /h /k%USE_COLS% type %OUTFILE% :skip3 rem ------------------------------------------------ rem Combinations of four colorspaces. rem goto skip4 set OUTFILE=%OUT_FMT:XX=4% del phbest84*.csv 2>nul for %%C in (%COLSPACES%) do ( for %%D in (%COLSPACES%) do ( if /I %%C LSS %%D ( for %%E in (%COLSPACES%) do ( if /I %%D LSS %%E ( for %%F in (%COLSPACES%) do ( if /I %%E LSS %%F ( call %PICTBAT%phCombin %%C %%D %%E %%F if ERRORLEVEL 1 exit /B 1 set INCSV=%PH_CSVDIR%\idPh!PH_COMBIN!.csv set OUTCSV=phbest!PH_COMBIN!.csv call %PICTBAT%phpThreshCsvBest !INCSV! !OUTCSV! !PH_PLUS! if ERRORLEVEL 1 exit /B 1 ) ) ) ) ) ) ) cat phbest84*.csv >%OUTFILE% cSort /p0 /i%OUTFILE% /o%OUTFILE% /u /r cSort /p0 /i%OUTFILE% /o%OUTFILE% /h /kMCCzero cProject /p0 /i%OUTFILE% /o%OUTFILE% /h /k%USE_COLS% type %OUTFILE% :skip4 endlocal
The scripts also use general-purpose programs such as cPrefix.exe, and I do not supply the source or binaries of these.
// Whether to use proportional difference. #define PROP_DIFF 0 #define MINIMIZE_WHAT pScores->MCCzero #include "scorePairs.inc" int main (int argc, char *argv []) { BOOL okay = ScorePairs (argv[1], FALSE); return (okay ? 0 : 1) ; }
#include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <math.h> #include <malloc.h> #include "ArgType.h" #define LineLen 10000 #ifdef MINIMIZE_WHAT #define FIND_MIN 1 #else #define FIND_MIN 0 #endif // Possibilities for MINIMIZE_WHAT: // #define MINIMIZE_WHAT pScores->FPFN // #define MINIMIZE_WHAT pScores->MCCzero typedef double ValueT; typedef struct { int Neighbour; ValueT Score; int GroupNum; } ObjectT; typedef struct { int nRecs; int nValsPerRec; char ** Names; ValueT * Values; ObjectT * Objects; ValueT minVal; ValueT maxVal; int nErrNrSrc; int nErrNMLo; ValueT hiMat; ValueT loNonMat; ValueT highest; int actualPos; int actualNeg; int truePos; int falsePos; int trueNeg; int falseNeg; //ValueT sumFalsePc; ValueT FPFN; ValueT MCC; ValueT MCCzero; } ScoresT; /* Note that the score between two objects is symmetrical, but the "nearest neghbour" relation may not be. */ static BOOL GetField (char ** pLine, char * sField, int * fldLen) // Returns whether another field was found. // Field may be empty. { //printf ("pLine={%s}", *pLine); strcpy (sField, *pLine); //printf ("pLine={%s}", *pLine); //printf (" sField={%s}", sField); if (!*sField) return FALSE; char * p = strchr (sField, ','); if (p) *p = '\0'; int nLen = strlen (sField); //printf ("nLen=%i ", nLen); *pLine += nLen; if (p) *pLine += 1; *fldLen = nLen; return TRUE; } static BOOL AllocScores (ScoresT * pScores, int nRecs, int nValues) { printf ("AllocScores\n"); pScores->nRecs = nRecs; pScores->nValsPerRec = nValues; pScores->Names = (char **) malloc (nRecs * sizeof (char *)); if (!pScores->Names) { printf ("oom Names"); return FALSE; } pScores->Values = (ValueT *) malloc (nRecs * nValues * sizeof (ValueT)); if (!pScores->Values) { free (pScores->Names); printf ("oom Values"); return FALSE; } pScores->Objects = (ObjectT *) malloc (nRecs * sizeof (ObjectT)); if (!pScores->Objects) { free (pScores->Values); free (pScores->Names); printf ("oom Objects"); return FALSE; } int i; for (i=0; i < nRecs; i++) { pScores->Names[i] = NULL; pScores->Objects[i].Neighbour = -1; pScores->Objects[i].Score = -1; pScores->Objects[i].GroupNum = -1; } for (i=0; i < nRecs*nValues; i++) pScores->Values[i] = 0.0; return TRUE; } static void DeAllocScores (ScoresT * pScores) { int i; for (i=0; i < pScores->nRecs; i++) { if (pScores->Names[i]) free (pScores->Names[i]); } free (pScores->Names); free (pScores->Values); free (pScores->Objects); pScores->nRecs = 0; pScores->nValsPerRec = 0; } static inline ValueT * pointValue (ScoresT * pScores, int recNum, int valNum) { if (recNum >= pScores->nRecs || valNum >= pScores->nValsPerRec) { exit (1); } return &(pScores->Values[recNum*pScores->nValsPerRec + valNum]); } static void DumpScores (ScoresT * pScores) { printf ("DumpScores nRecs=%i valsPerRec=%i\n", pScores->nRecs, pScores->nValsPerRec); int i, j; for (i=0; i < pScores->nRecs; i++) { printf ("%i: ", i); if (pScores->Names[i]) printf ("[%s]", pScores->Names[i]); for (j=0; j < pScores->nValsPerRec; j++) { printf (", %g", *pointValue (pScores, i, j)); } printf ("\n"); } } static void DumpNeighbours (ScoresT * pScores) { int i; for (i=0; i < pScores->nRecs; i++) { printf ("%i: %s, ", i, pScores->Names[i]); int neighb = pScores->Objects[i].Neighbour; if (neighb >= 0) { printf ("%s, %g", pScores->Names[neighb], pScores->Objects[i].Score); } else { printf ("unknown, unknown"); } printf (",%i", pScores->Objects[i].GroupNum); printf ("\n"); } } static BOOL ReadFile (FILE * fin, int * nRecs, int * nValues, ScoresT * pScores) { printf ("ReadFile %s\n", pScores ? "saving" : "not saving"); BOOL okay = TRUE; char NextLine [LineLen]; char Field [LineLen]; fseek (fin, 0, SEEK_SET); int nLines = 0; int expectFlds = -1; if (pScores) { pScores->minVal = 9e9; pScores->maxVal = -9e9; } while (fgets (NextLine, LineLen, fin)) { if (!okay) continue; //printf ("%s\n", NextLine); char *p = NextLine; int nFlds=0; int fldLen; while (GetField (&p, Field, &fldLen)) { // printf (" [%s]\n", Field); if (pScores) { if (nFlds == 0) { pScores->Names[nLines] = (char *) malloc ((fldLen+1) * sizeof (char)); if (!pScores->Names[nLines]) { okay = FALSE; } else { strcpy (pScores->Names[nLines], Field); } } else { ValueT * pv = pointValue (pScores, nLines, nFlds-1); *pv = atof (Field); //printf ("*pv=%g ", *pv); if (pScores->minVal > *pv) pScores->minVal = *pv; if (pScores->maxVal < *pv) pScores->maxVal = *pv; } } nFlds++; } if (expectFlds == -1) expectFlds = nFlds; else if (nFlds != expectFlds) { okay = FALSE; } nLines++; } if (expectFlds == -1) okay = FALSE; *nRecs = nLines; *nValues = expectFlds - 1; return okay; } /* Infile is a CSV text file without quotes, no header, one object (eg image) per line. First field is a name (typically an image filename). This is followed by (n) floating-point values. All objects must have same number of values. Field separators are commas, with no spaces. Works by calculating score for every possible pair of objects. score = RMS(difference) where difference is a value in one object minus the corresponding value in the other object. RMS is root-mean-square */ static inline ValueT CompareRecs (ScoresT * pScores, int r0, int r1) { ValueT v = 0; ValueT * pv0 = &(pScores->Values[r0*pScores->nValsPerRec]); ValueT * pv1 = &(pScores->Values[r1*pScores->nValsPerRec]); int i; for (i=0; i < pScores->nValsPerRec; i++) { #if PROP_DIFF==0 ValueT diff = (*pv1 - *pv0); #endif #if PROP_DIFF==1 // Optimum MIN_DIV was found by trial and error. #define MIN_DIV 0.25 ValueT div = *pv1 + *pv0; if (abs(div) < MIN_DIV) div = (div < 0)? -MIN_DIV : +MIN_DIV; ValueT diff = (*pv1 - *pv0) / div; /* ValueT v0 = *pv0; ValueT v1 = *pv1; if (v0 < 0) v0 = 0; if (v1 < 0) v1 = 0; div = v1 + v0; diff = (v1 - v0); */ #endif v += diff*diff; pv0++; pv1++; } return sqrt (v / pScores->nValsPerRec); } static inline void FindBestScore (ScoresT * pScores, int r0) { printf ("FindBestScore\n"); ValueT BestScore = 9e+9; int BestRec = -1; int i; for (i=0; i < pScores->nRecs; i++) { if (i != r0) { ValueT vt = CompareRecs (pScores, r0, i); if (BestScore > vt) { BestScore = vt; BestRec = i; } } } if (BestRec == -1) { exit (1); } printf ("%s,%s,%g\n", pScores->Names[r0], pScores->Names[BestRec], BestScore); pScores->Objects[r0].Neighbour = BestRec; pScores->Objects[r0].Score = BestScore; } static inline BOOL IsSourceName (ScoresT * pScores, int r1) { char *p = strchr (pScores->Names[r1], '_'); if (p) return FALSE; else return TRUE; } static inline BOOL DoNamesMatch (ScoresT * pScores, int r0, int r1) { #define MAX_LEN 100 char name0[MAX_LEN]; char name1[MAX_LEN]; strncpy (name0, pScores->Names[r0], MAX_LEN); strncpy (name1, pScores->Names[r1], MAX_LEN); char * p; p = strchr (name0, '.'); if (p) *p = '\0'; p = strchr (name1, '.'); if (p) *p = '\0'; p = strchr (name0, '_'); if (p) *p = '\0'; p = strchr (name1, '_'); if (p) *p = '\0'; p = strchr (name0, '-'); if (p) *p = '\0'; p = strchr (name1, '-'); if (p) *p = '\0'; return (strcmp (name0, name1) == 0); } static inline void FindFourScores (ScoresT * pScores, int r0) // For record r0, finds four scores: // highest and lowest with matching name, and highest and lowest with non-matching name. // "Name matching" is up to first underscore. { printf ("FindFourScores\n"); ValueT MatchHi = -1, MatchLo = 9e+9, NonMatchHi = -1, NonMatchLo = 9e+9; int rmlo=-1, rmhi=-1, rnmlo=-1, rnmhi=-1; ValueT SrcScore = 9e+9; int nSrc = -1; int i; for (i=0; i < pScores->nRecs; i++) { if (i != r0) { ValueT vt = CompareRecs (pScores, r0, i); if (IsSourceName (pScores, i)) { if (SrcScore > vt) { SrcScore = vt; nSrc = i; } } if (pScores->highest < vt) pScores->highest = vt; if (DoNamesMatch (pScores, r0, i)) { if (MatchHi < vt) { MatchHi = vt; rmhi = i; } if (MatchLo > vt) { MatchLo = vt; rmlo = i; } if (pScores->hiMat < vt) pScores->hiMat = vt; } else { if (NonMatchHi < vt) { NonMatchHi = vt; rnmhi = i; } if (NonMatchLo > vt) { NonMatchLo = vt; rnmlo = i; } if (pScores->loNonMat > vt) pScores->loNonMat = vt; } } } char * errNrSrc = ""; char * errNMLo = ""; if (nSrc < 0) { printf ("nSrc < 0\n"); exit (1); } if (!DoNamesMatch (pScores, r0, nSrc)) { errNrSrc = "**"; pScores->nErrNrSrc++; } if (NonMatchLo <= MatchLo) { errNMLo = "**"; pScores->nErrNMLo++; } printf ("%s\n", pScores->Names[r0]); if (rmlo >= 0) printf (" MatLo %s %g\n", pScores->Names[rmlo], MatchLo); if (rmhi >= 0) printf (" MatHi %s %g\n", pScores->Names[rmhi], MatchHi); if (rnmlo >= 0) printf (" NonLo %s %g %s\n", pScores->Names[rnmlo], NonMatchLo, errNMLo); if (rnmhi >= 0) printf (" NonHi %s %g\n", pScores->Names[rnmhi], NonMatchHi); if (nSrc >= 0) printf (" NrSrc %s %g %s\n", pScores->Names[nSrc], SrcScore, errNrSrc); } static void FindBestScores (ScoresT * pScores) { int i; for (i=0; i < pScores->nRecs; i++) { //FindBestScore (pScores, i); FindFourScores (pScores, i); } } static void FindGroup (ScoresT * pScores, int r0) { // Visit all the objects to find the lowest object number. //printf ("\nFindGroup %i: ", r0); int lowest = 99999; int n = r0; while (n != lowest && pScores->Objects[n].GroupNum != lowest) { if (lowest > n) lowest = n; if (pScores->Objects[n].GroupNum < 0) { pScores->Objects[n].GroupNum = lowest; } else if (pScores->Objects[n].GroupNum > lowest) { pScores->Objects[n].GroupNum = lowest; } else if (pScores->Objects[n].GroupNum < lowest) { lowest = pScores->Objects[n].GroupNum; } n = pScores->Objects[n].Neighbour; //printf ("low=%i n=%i ", lowest, n); } n = r0; while (pScores->Objects[n].GroupNum != lowest) { pScores->Objects[n].GroupNum = lowest; n = pScores->Objects[n].Neighbour; } } static void FindGroups (ScoresT * pScores) { int i; for (i=0; i < pScores->nRecs; i++) { if (pScores->Objects[i].GroupNum < 0) FindGroup (pScores, i); } } static void FindMatches (ScoresT * pScores, ValueT threshold) // Compare every images with every other image. // If the RMS < threshold, declare them a match. // From the names, we know if they should actually be a match. // So we can count false positives and false negatives. { pScores->actualPos = 0; pScores->actualNeg = 0; pScores->truePos = 0; pScores->falsePos = 0; pScores->trueNeg = 0; pScores->falseNeg = 0; //pScores->sumFalsePc = 0.0; int i, j; for (i=0; i < pScores->nRecs; i++) { for (j=0; j < pScores->nRecs; j++) { if (i != j) { BOOL actualPos = DoNamesMatch (pScores, i, j); ValueT vt = CompareRecs (pScores, i, j); if (vt < threshold) { if (actualPos) { pScores->actualPos++; pScores->truePos++; } else { pScores->actualNeg++; pScores->falsePos++; } } else { if (actualPos) { pScores->actualPos++; pScores->falseNeg++; } else { pScores->actualNeg++; pScores->trueNeg++; } } } } } printf ("threshold=%g", threshold); printf (" truePos=%i", pScores->truePos); printf (" trueNeg=%i", pScores->trueNeg); printf (" falsePos=%i", pScores->falsePos); printf (" falseNeg=%i", pScores->falseNeg); printf (" actualPos=%i", pScores->actualPos); printf (" actualNeg=%i", pScores->actualNeg); /*--- ValueT fmPc = 100.0 * pScores->falsePos / (ValueT)pScores->actualPos; ValueT fmnPc = 100.0 * pScores->falseNeg / (ValueT)pScores->actualNeg; pScores->sumFalsePc = fmPc + fmnPc; printf ("falsePosPc=%g ", fmPc); printf ("falseNegPc=%g ", fmnPc); printf ("sumFalsePc=%g ", pScores->sumFalsePc); ---*/ ValueT FPrate = pScores->falsePos / (ValueT)pScores->actualNeg; ValueT FNrate = pScores->falseNeg / (ValueT)pScores->actualPos; pScores->FPFN = FPrate + FNrate; ValueT d = (ValueT)(pScores->truePos + pScores->falsePos) * (ValueT)(pScores->truePos + pScores->falseNeg) * (ValueT)(pScores->trueNeg + pScores->falsePos) * (ValueT)(pScores->trueNeg + pScores->falseNeg); if (d == 0) d = 1; ValueT n = ((ValueT)pScores->truePos * (ValueT)pScores->trueNeg) - ((ValueT)pScores->falsePos * (ValueT)pScores->falseNeg); pScores->MCC = n / sqrt (d); pScores->MCCzero = 1 - pScores->MCC; printf (" FPrate=%g", FPrate); printf (" FNrate=%g", FNrate); printf (" FPFN=%g", pScores->FPFN); printf (" MCC=%g", pScores->MCC); printf (" MCCzero=%g", pScores->MCCzero); printf ("\n"); } static void FindRangeFalseSumPc (ScoresT * pScores, ValueT t0, ValueT t1, int nSteps) { printf ("FindRangeFalseSumPc\n"); if (t0 > t1) { ValueT T = t0; t0 = t1; t1 = T; } ValueT tStep = (t1 - t0) / (ValueT)nSteps; ValueT t; for (t = t0; t <= t1; t += tStep) { FindMatches (pScores, t); } } #if FIND_MIN==1 static inline int sign0 (ValueT v) { #define EPSILON 1e-6 if (fabs(v) < EPSILON) return 0; return (v > 0) ? +1 : -1; } static void FindThreshForMin (ScoresT * pScores) { printf ("FindThreshForMin\n"); #define epsilon 0.00001 ValueT t0 = pScores->loNonMat; ValueT t3 = pScores->hiMat; ValueT t1 = t0 + (t3 - t0) / 3.0; ValueT t2 = t0 + (t3 - t0) * 2/3.0; FindMatches (pScores, t0); ValueT sumPc0 = MINIMIZE_WHAT; FindMatches (pScores, t1); ValueT sumPc1 = MINIMIZE_WHAT; FindMatches (pScores, t2); ValueT sumPc2 = MINIMIZE_WHAT; FindMatches (pScores, t3); ValueT sumPc3 = MINIMIZE_WHAT; BOOL done = FALSE; int nIter = 0; while (!done) { printf ("t=%g, %g, %g, %g ", t0, t1, t2, t3); printf ("sumPc=%g, %g, %g, %g\n", sumPc0, sumPc1, sumPc2, sumPc3); /*--- if (sumPc1 > sumPc0 && sumPc1 > sumPc2) { printf ("Bust\n"); done = TRUE; } else if (fabs(t0 - t2) < epsilon) { printf ("Done\n"); done = TRUE; } else if (sumPc0 > sumPc2) { // Required t is between t1 and t2 sumPc0 = sumPc1; t0 = t1; } else if (sumPc0 < sumPc2) { // Required t is between t0 and t1 sumPc2 = sumPc1; t2 = t1; } else { printf ("Huh?\n"); done = TRUE; } t1 = (t0 + t2) / 2.0; ---*/ int s01 = sign0 (sumPc1 - sumPc0); int s12 = sign0 (sumPc2 - sumPc1); int s23 = sign0 (sumPc3 - sumPc2); //printf ("sign %i, %i, %i\n", s01, s12, s23); BOOL dropFirst = FALSE; BOOL dropLast = FALSE; if (s01==0 && s12==0 && s23==0) { printf ("Done\n"); done = TRUE; } else if (s01==-1 && s12==-1) { dropFirst = TRUE; } else if (s23==+1 && s12==+1) { dropLast = TRUE; } if (!done) { if (!dropFirst && !dropLast) { if (sumPc0 >= sumPc3) dropFirst = TRUE; else dropLast = TRUE; } if (dropFirst) { //printf ("dropFirst\n"); t0 = t1; sumPc0 = sumPc1; } else if (dropLast) { //printf ("dropLast\n"); t3 = t2; sumPc3 = sumPc2; } t1 = t0 + (t3 - t0) / 3.0; t2 = t0 + (t3 - t0) * 2/3.0; FindMatches (pScores, t1); sumPc1 = MINIMIZE_WHAT; FindMatches (pScores, t2); sumPc2 = MINIMIZE_WHAT; } nIter++; } printf ("nIter=%i\n", nIter); printf ("best at threshold=%g MinValue=%g\n", t1, sumPc1); FindMatches (pScores, t1); } #endif // FIND_MIN==1 static BOOL ScorePairs (char * InFile, BOOL outAll) // Returns whether okay. { if (!*InFile) { printf ("ScorePairs needs InFile"); return FALSE; } FILE * fin; BOOL okay = TRUE; if (strcmp (InFile, "-")==0) { fin = stdin; } else { fin = fopen (InFile, "rt"); } if (fin == NULL) { printf ("Can't open input file\n"); okay = FALSE; return okay; } int nRecs, nValues; if (!ReadFile (fin, &nRecs, &nValues, NULL)) { okay = FALSE; return okay; } printf ("nRecs=%i nValues=%i\n", nRecs, nValues); ScoresT Scores; okay = AllocScores (&Scores, nRecs, nValues); if (!okay) return FALSE; Scores.minVal = 0; Scores.maxVal = 0; if (!ReadFile (fin, &nRecs, &nValues, &Scores)) okay = FALSE; /* DumpScores (&Scores); */ Scores.nErrNrSrc = 0; Scores.nErrNMLo = 0; Scores.hiMat = 0; Scores.loNonMat = 99e99; Scores.highest = 0; FindBestScores (&Scores); printf ("nErrNMLo = %i", Scores.nErrNMLo); printf (", nErrNrSrc = %i", Scores.nErrNrSrc); printf (", highest = %g", Scores.highest); printf (", hiMat = %g", Scores.hiMat); printf (", loNonMat = %g\n", Scores.loNonMat); printf ("minVal = %g", Scores.minVal); printf (", maxVal = %g\n", Scores.maxVal); /* DumpNeighbours (&Scores); FindGroups (&Scores); DumpNeighbours (&Scores); */ //FindMatches (&Scores, Scores.hiMat); //FindMatches (&Scores, (Scores.hiMat+Scores.loNonMat)/2.0); //FindMatches (&Scores, Scores.loNonMat); #if FIND_MIN==0 FindRangeFalseSumPc (&Scores, Scores.loNonMat, Scores.hiMat, 10); #endif #if FIND_MIN==1 FindThreshForMin (&Scores); #endif DeAllocScores (&Scores); fclose (fin); return okay; } //int main (int argc, char *argv []) //{ // BOOL okay = ScorePairs (argv[1], "out.csv", FALSE); // // return (okay ? 0 : 1) ; //}
All images on this page were created by the commands shown, using:
%IM%identify -version
Version: ImageMagick 6.9.5-3 Q16 x86 2016-07-22 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 jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib
To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG or TIFF or MIFF to JPG.
Source file for this web page is phashthresh.h1. To re-create this web page, run "procH1 phashthresh".
This page, including the images except where shown otherwise, 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 1-September-2016.
Page created 15-Aug-2022 16:28:32.
Copyright © 2022 Alan Gibson.