R Under development (unstable) (2025-01-18 r87593 ucrt) -- "Unsuffered Consequences" Copyright (C) 2025 The R Foundation for Statistical Computing Platform: x86_64-w64-mingw32/x64 R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > > # tests xyYtoMunsell() and MunsellToxyY() over special colors > > # tests sRGBtoMunsell() and MunsellTosRGB() over special colors > > > library( munsellinterpol ) > > options( width=144 ) > > printf <- function( msg, ... ) + { + mess = sprintf( msg[1], ... ) # should this really be msg[1] ? + cat( mess, '\n' ) #, file=stderr() ) + } > > # returns time in seconds, from an arbitrary origin > gettime <- function() + { + if( requireNamespace('microbenchmark') ) + return( microbenchmark::get_nanotime() * 1.e-9 ) + else + return( as.double( base::Sys.time() ) ) + } > > > testCentroids <- function() + { + # path = "../inst/extdata/Centrals_ISCC_NBS.txt" + path = system.file( "extdata/Centroids_ISCC-NBS.txt", package='munsellinterpol' ) + + df = read.table( path, header=T, sep='\t', stringsAsFactors=F ) + + printf( "----------- testCentroids() %d samples -----------------------", nrow(df) ) + + # printf( "Testing centroids..." ) + + # go forwards HVC -> xyY + + HVC = HVCfromMunsellName( df$MunsellSpec ) + + time_start = gettime() + + res = MunsellToxyY( HVC, warn=FALSE ) + + time_elapsed = gettime() - time_start + + failures = sum( is.na(res$xyY[ ,1]) ) + + printf( "There were %d forward failures, out of %d samples. time=%g sec. %g/sample.", + failures, nrow(df), time_elapsed, time_elapsed/nrow(HVC) ) + + + if( 0 < failures ) return(FALSE) + + # now go backwards xyY -> HVC + time_start = gettime() + + res = xyYtoMunsell( res$xyY, perf=TRUE ) + + time_elapsed = gettime() - time_start + + failures = sum( is.na(res$HVC[ ,1]) ) + + printf( "There were %d reverse failures, out of %d samples. time=%g sec. %g/sample.", + failures, nrow(HVC), time_elapsed, time_elapsed/nrow(HVC) ) + + printf( "Performance:") + cat( "Time Elapsed 5-number summary: " ) + cat( fivenum(res$time.elapsed,na.rm=T), '\n' ) + cat( "Iterations 5-number summary: " ) + cat( fivenum(res$iterations,na.rm=T), '\n' ) + cat( "Evaluations 5-number summary: " ) + cat( fivenum(res$evaluations,na.rm=T), '\n' ) + + # compare HVC and res$HVC + tol = 0.01 + HVC.delta = abs(HVC - res$HVC) + HVC.delta[ ,1] = pmin( HVC.delta[ ,1], 100 - HVC.delta[ ,1] ) + err = rowSums( HVC.delta ) + idx = which.max( err ) + df = data.frame( row.names=1 ) + df$HVC = HVC[ idx, , drop=F ] + df$xyY = res$xyY[ idx, , drop=F ] + df$HVC.back = res$HVC[ idx, , drop=F ] + printf( "Maximum round-trip error = %g (tol=%g).", err[idx], tol ) + print( df ) + + # how many exceeded the tolerance + count = sum( tol < err ) + if( 0 < count ) + printf( "%d round-trip errors exceeded %g.", count, tol ) + + if( 0 < failures || 0 < count ) return(FALSE) + + return( TRUE ) + } > > > testOptimals <- function() + { + path = system.file( "extdata/OptimalColorsForIlluminantC.txt", package='munsellinterpol' ) + + df = read.table( path, header=T ) + + printf( "--------- testOptimals() %d samples -----------------------", nrow(df) ) + + xyY = as.matrix( df ) + + osname = Sys.info()[ 'sysname' ] + solaris = grepl( 'sun', osname, ignore.case=TRUE ) + + out = TRUE + + for( hcinterp in c('bilin','bicub') ) + { + for( vinterp in c('lin','cub') ) + { + # go backwards xyY -> HVC + printf( "----- hcinterp='%s', vinterp='%s' -----", hcinterp, vinterp ) + + time_start = gettime() + + res = xyYtoMunsell( xyY, warn=FALSE, perf=TRUE, hcinterp=hcinterp, vinterp=vinterp ) + + time_elapsed = gettime() - time_start + + # print( res ) + # return(FALSE) + + HVC = res$HVC + + mask = is.na( HVC[ ,1] ) + + failures = sum( mask ) + + printf( "testOptimals(). There were %d inversion failures, out of %d samples. time=%g sec. %g/sample. OS=%s", + failures, nrow(xyY), time_elapsed, time_elapsed/nrow(xyY), osname ) + + # in 2018, solaris started to give 5 iteration failures in CRAN testing + # in 2019 (R v 3.6.1), the same thing happened with fedora (both clang and gcc). + # So for safety in the future, just set limit to 5 ! + limit = 5 # ifelse( solaris, 5, 0 ) # with solaris, 5 errors were detected in CRAN testing + + if( limit < failures ) + { + colnames(xyY) = c('x','y','Y') + colnames(HVC) = c('H','V','C') + df = data.frame( row.names=1:failures ) + df$xyY = xyY[mask, , drop=FALSE] + df$HVC = HVC[mask, , drop=FALSE] + print( df ) + + #print( res ) + out = FALSE + } + + printf( "Performance:") + cat( "Time Elapsed 5-number summary: " ) + cat( fivenum(res$time.elapsed,na.rm=T), '\n' ) + cat( "Iterations 5-number summary: " ) + cat( fivenum(res$iterations,na.rm=T), '\n' ) + cat( "Evaluations 5-number summary: " ) + cat( fivenum(res$evaluations,na.rm=T), '\n' ) + + if( max( res$iterations, na.rm=T ) == 100 ) + { + idx = which( res$iterations == 100 ) # ; print(idx) + printf( "There were %d samples that failed to converge.", length(idx) ) + df = res[ idx, , drop=FALSE ] + print(df) + } + + if( out ) + { + # do round-trip + xyY.back = MunsellToxyY( HVC, warn=FALSE, hcinterp=hcinterp, vinterp=vinterp )$xyY + + delta = rowSums( abs(xyY - xyY.back) ) + + # ignore pure black + mask = xyY[ ,3]==0 & xyY.back[ ,3]==0 + delta[mask] = 0 + + cat( "Round trip xyY -> HVC -> xyY inversion error 5-number summary: " ) + cat( fivenum(delta), '\n' ) + + idx = which.max( delta ) + df = data.frame( row.names=1 ) + df$xyY = xyY[ idx, , drop=F ] + df$HVC = HVC[ idx, , drop=F ] + df$xyY.back = xyY.back[ idx, , drop=F ] + printf( "Maximum inversion error occurs for this one:" ) + print( df ) + } + } + } + + return( out ) + } > > > > testReals <- function() + { + df.real = subset( Munsell2xy, real==TRUE ) + + printf( "---------------- testReals() %d samples -----------------------", nrow(df.real) ) + + HVC = cbind( df.real$H, df.real$V, df.real$C ) + xyY = cbind( df.real$x, df.real$y, YfromV(df.real$V) ) + + # go backwards xyY -> HVC + time_start = gettime() + + res = xyYtoMunsell( xyY, warn=FALSE, perf=TRUE ) + + time_elapsed = gettime() - time_start + + mask = is.na( res$HVC[ ,1] ) + + failures = sum( mask ) + + printf( "testReals(). There were %d inversion failures, out of %d samples. time=%g sec. %g/sample.", + failures, nrow(xyY), time_elapsed, time_elapsed/nrow(xyY) ) + + + if( 0 < failures ) + { + colnames(HVC) = c('H','V','C') + colnames(xyY) = c('x','y','Y') + df = data.frame( row.names=MunsellNameFromHVC( HVC[mask, ] ) ) + df$HVC = HVC[mask, ] + df$xyY = xyY[mask, ] + print( df ) + } + + printf( "Performance:") + cat( "Time Elapsed 5-number summary: " ) + cat( fivenum(res$time.elapsed,na.rm=T), '\n' ) + cat( "Iterations 5-number summary: " ) + cat( fivenum(res$iterations,na.rm=T), '\n' ) + cat( "Evaluations 5-number summary: " ) + cat( fivenum(res$evaluations,na.rm=T), '\n' ) + + + + # compare HVC and res$HVC + HVC.delta = abs(HVC - res$HVC) + HVC.delta[ ,1] = pmin( HVC.delta[ ,1], 100 - HVC.delta[ ,1] ) + delta = rowSums( HVC.delta ) + cat( "Round trip HVC -> xyY -> HVC inversion error 5-number summary: " ) + cat( fivenum(delta), '\n' ) + idx = which.max( delta ) + df = data.frame( row.names=1 ) + df$HVC = HVC[ idx, , drop=F ] + df$xyY = xyY[ idx, , drop=F ] + df$HVC.back = res$HVC[ idx, , drop=F ] + printf( "Maximum inversion error occurs for this one:" ) + print( df ) + + + return( failures == 0 ) + } > > > testNeutrals <- function() + { + # check that Chroma is small, and finite + RGB = matrix( 0:255, 256, 3 ) + colnames(RGB) = c('R','G','B') + + printf( "----------- testNeutrals() %d samples -----------------------", nrow(RGB) ) + + HVC = sRGBtoMunsell( RGB ) + + chroma = HVC[ ,3] + mask = is.na(chroma) + failures = sum(mask) + if( 0 < failures ) + { + printf( "testNeutrals(). There were %d failures to convert.", failures ) + df = data.frame( row.names=which(mask) ) + df$RGB = RGB[mask, ] + print( df ) + } + + tol = 0 # 1.e-13 changed to 0 in v 3.0-0 + mask = (is.finite(chroma) & tol < chroma) + count = sum( mask ) + if( 0 < count ) + { + printf( "testNeutrals(). There were %d chromas > %g.", count, tol ) + df = data.frame( row.names=which(mask) ) + df$RGB = RGB[mask, ] + df$Chroma = chroma[mask] + print( df ) + } + + # now go back + res = MunsellTosRGB( HVC ) + + RGB.delta = abs(RGB - res$RGB) + delta = rowSums( RGB.delta ) + cat( "Round-trip error (RGB -> HVC -> RGB) 5-number summary: " ) + cat( fivenum(delta), '\n' ) + + return( failures==0 && count==0 ) + } > > > testNearNeutrals <- function() + { + # check that Chroma is small, and finite + RGB = matrix( 0, 0, 3 ) + colnames(RGB) = c('R','G','B') + + for( k in 10:255 ) + { + RGB = rbind( RGB, matrix(k,3,3) - diag(3) ) + } + + printf( "------- testNearNeutrals() %d samples -----------------------", nrow(RGB) ) + + + HVC = sRGBtoMunsell( RGB ) + + chroma = HVC[ ,3] + mask = is.na(chroma) + failures = sum(mask) + if( 0 < failures ) + { + printf( "testNearNeutrals(). There were %d failures to convert.", failures ) + df = data.frame( row.names=which(mask) ) + df$RGB = RGB[mask, ] + print( df ) + return(FALSE) + } + + tol = 0.30 + mask = (is.finite(chroma) & tol < chroma) + count = sum( mask ) + if( 0 < count ) + { + printf( "testNearNeutrals(). There were %d chromas > %g.", count, tol ) + df = data.frame( row.names=which(mask) ) + df$RGB = RGB[mask, ] + df$Chroma = chroma[mask] + print( df ) + return(FALSE) + } + + + # now go back + res = MunsellTosRGB( HVC ) + RGB.delta = abs(RGB - res$RGB) + delta = rowSums( RGB.delta ) + cat( "Round-trip error (RGB->HVC->RGB) 5-number summary: " ) + cat( fivenum(delta), '\n' ) + + tol = 0.005 + if( tol < max(delta) ) + { + printf( "Maximum round-trip error = %g > %g.", max(delta), tol ) + return(FALSE) + } + + return( TRUE ) + } > > testDarks <- function() + { + rgbmax = 8 # 10 # 5 + + RGB = as.matrix( expand.grid( R=0:rgbmax, G=0:rgbmax, B=0:rgbmax ) ) + + printf( "--------------------- testDarks() %d samples -----------------------", nrow(RGB) ) + + HVC = sRGBtoMunsell( RGB ) + + chroma = HVC[ ,3] + mask = is.na(chroma) + failures = sum(mask) + if( 0 < failures ) + { + printf( "testDarks(). There were %d failures to convert.", failures ) + df = data.frame( row.names=which(mask) ) + df$RGB = RGB[mask, , drop=FALSE ] + print( df ) + return(FALSE) + } + + # now go back + res = MunsellTosRGB( HVC ) + RGB.delta = abs(RGB - res$RGB) + delta = rowSums( RGB.delta ) + cat( "Round-trip error (RGB->HVC->RGB) 5-number summary: " ) + cat( fivenum(delta), '\n' ) + + return( failures==0 ) + } > > > { + x = gettime() # load microbenchmark + + + if( testCentroids() ) + printf( "testCentroids() passed." ) + else + stop( "testCentroids() failed !", call.=FALSE ) + + + if( testNeutrals() ) + printf( "testNeutrals() passed." ) + else + stop( "testNeutrals() failed !", call.=FALSE ) + + if( testNearNeutrals() ) + printf( "testNearNeutrals() passed." ) + else + stop( "testNearNeutrals() failed !", call.=FALSE ) + + if( testDarks() ) + printf( "testDarks() passed." ) + else + stop( "testDarks() failed !", call.=FALSE ) + + if( testReals() ) + printf( "testReals() passed." ) + else + stop( "testReals() failed !", call.=FALSE ) + + if( testOptimals() ) + printf( "testOptimals() passed." ) + else + stop( "testOptimals() failed !", call.=FALSE ) + + + + printf( "Passed all Munsell Transforms tests !" ) + } ----------- testCentroids() 267 samples ----------------------- There were 0 forward failures, out of 267 samples. time=0.143359 sec. 0.000536927/sample. There were 0 reverse failures, out of 267 samples. time=1.02493 sec. 0.00383871/sample. Performance: Time Elapsed 5-number summary: 5.200505e-06 0.0032144 0.003622 0.004146249 0.0283292 Iterations 5-number summary: 2 3 3 4 5 Evaluations 5-number summary: 8 11 11 14 17 Maximum round-trip error = 0.00728479 (tol=0.01). HVC.H HVC.V HVC.C xyY.x xyY.y xyY.Y HVC.back.H HVC.back.V HVC.back.C 1 23.00 5.50 0.06 0.3112943 0.3174985 23.9680791 23.00726754 5.50000000 0.05998275 testCentroids() passed. ----------- testNeutrals() 256 samples ----------------------- Round-trip error (RGB -> HVC -> RGB) 5-number summary: 0 1.958787e-08 4.648795e-08 7.315473e-08 1.73029e-07 testNeutrals() passed. ------- testNearNeutrals() 738 samples ----------------------- Round-trip error (RGB->HVC->RGB) 5-number summary: 8.470828e-09 5.922419e-06 4.008284e-05 0.0001609282 0.002403704 testNearNeutrals() passed. --------------------- testDarks() 729 samples ----------------------- Round-trip error (RGB->HVC->RGB) 5-number summary: 0 1.615927e-07 5.110979e-07 4.897153e-06 0.0001205296 testDarks() passed. ---------------- testReals() 3089 samples ----------------------- testReals(). There were 0 inversion failures, out of 3089 samples. time=8.96168 sec. 0.00290116/sample. Performance: Time Elapsed 5-number summary: 0.0018255 0.0023889 0.0029104 0.0029903 0.0307802 Iterations 5-number summary: 2 3 4 4 7 Evaluations 5-number summary: 8 11 14 14 23 Round trip HVC -> xyY -> HVC inversion error 5-number summary: 5.892797e-11 5.333471e-08 1.86361e-06 2.367377e-05 0.0008028285 Maximum inversion error occurs for this one: HVC.1 HVC.2 HVC.3 xyY.1 xyY.2 xyY.3 HVC.back.H HVC.back.V HVC.back.C 1 60 9 2 0.29070 0.31590 76.69559 60.000798 9.000000 2.000005 testReals() passed. --------- testOptimals() 994 samples ----------------------- ----- hcinterp='bilin', vinterp='lin' ----- testOptimals(). There were 0 inversion failures, out of 994 samples. time=3.96703 sec. 0.00399098/sample. OS=Windows Performance: Time Elapsed 5-number summary: 1.069997e-05 0.003324499 0.00381275 0.004245199 0.0353326 Iterations 5-number summary: 2 3 3 4 5 Evaluations 5-number summary: 8 11 11 14 17 Round trip xyY -> HVC -> xyY inversion error 5-number summary: 0 5.018656e-09 1.970463e-08 1.486058e-07 2.053347e-06 Maximum inversion error occurs for this one: xyY.x xyY.y xyY.Y HVC.H HVC.V HVC.C xyY.back.x xyY.back.y xyY.back.Y 1 0.19586 0.10683 17.81000 79.475725 4.829257 24.347855 0.1958589 0.1068290 17.8100000 ----- hcinterp='bilin', vinterp='cub' ----- Error in iH:(iH + 1) : NA/NaN argument Error in iH:(iH + 1) : NA/NaN argument Error in iH:(iH + 1) : NA/NaN argument Error in iH:(iH + 1) : NA/NaN argument Error in iH:(iH + 1) : NA/NaN argument testOptimals(). There were 5 inversion failures, out of 994 samples. time=4.85741 sec. 0.00488673/sample. OS=Windows Performance: Time Elapsed 5-number summary: 9.699725e-06 0.0039034 0.004631801 0.0049986 0.0818039 Iterations 5-number summary: 2 3 3 4 6 Evaluations 5-number summary: 8 11 11 14 20 Round trip xyY -> HVC -> xyY inversion error 5-number summary: 0 5.184237e-09 1.808143e-08 1.4736e-07 2.017029e-06 Maximum inversion error occurs for this one: xyY.x xyY.y xyY.Y HVC.H HVC.V HVC.C xyY.back.x xyY.back.y xyY.back.Y 1 0.24813 0.06337 8.55260 84.424134 3.457032 34.260334 0.24812887 0.06337088 8.55260000 ----- hcinterp='bicub', vinterp='lin' ----- testOptimals(). There were 0 inversion failures, out of 994 samples. time=4.97 sec. 0.005/sample. OS=Windows Performance: Time Elapsed 5-number summary: 1.010019e-05 0.004061501 0.00454315 0.005363599 0.0435314 Iterations 5-number summary: 2 3 4 4 6 Evaluations 5-number summary: 8 11 14 14 20 Round trip xyY -> HVC -> xyY inversion error 5-number summary: 0 5.598911e-09 2.052519e-08 1.851469e-07 2.226422e-06 Maximum inversion error occurs for this one: xyY.x xyY.y xyY.Y HVC.H HVC.V HVC.C xyY.back.x xyY.back.y xyY.back.Y 1 0.301670 0.081802 8.746800 87.960039 3.494055 31.490646 0.30166890 0.08180088 8.74680001 ----- hcinterp='bicub', vinterp='cub' ----- Error in max(iH - 1, 1):min(iH + 2, length(H.vector)) : NA/NaN argument Error in max(iH - 1, 1):min(iH + 2, length(H.vector)) : NA/NaN argument Error in max(iH - 1, 1):min(iH + 2, length(H.vector)) : NA/NaN argument Error in max(iH - 1, 1):min(iH + 2, length(H.vector)) : NA/NaN argument Error in max(iH - 1, 1):min(iH + 2, length(H.vector)) : NA/NaN argument testOptimals(). There were 5 inversion failures, out of 994 samples. time=4.57192 sec. 0.00459951/sample. OS=Windows Performance: Time Elapsed 5-number summary: 1.639966e-05 0.0036645 0.0041761 0.0050576 0.0153951 Iterations 5-number summary: 2 3 4 4 7 Evaluations 5-number summary: 8 11 14 14 23 Round trip xyY -> HVC -> xyY inversion error 5-number summary: 0 5.594705e-09 1.974002e-08 1.718229e-07 1.953349e-06 Maximum inversion error occurs for this one: xyY.x xyY.y xyY.Y HVC.H HVC.V HVC.C xyY.back.x xyY.back.y xyY.back.Y 1 0.47928 0.51513 77.45300 25.839890 9.035888 20.333109 0.479281 0.515131 77.453000 testOptimals() passed. Passed all Munsell Transforms tests ! > > proc.time() user system elapsed 38.79 0.23 39.03