This script extracts reflectance spectra of a set of art pigments
from a VISNIR hyperspectral image acquired with a demo unit of the
Specim IQ system in HSILab premises on 20180918:
- Specim IQ white reference card.
- Ocre (Ochre)
- Ocre 800 (Ochre heated to 800ºC)
- Vermell (Iron Oxide Red)
- Red 800 (Iron Oxide Red heated to 800ºC)
- Terra (green earth)
- Terra Verda 800 (green earth heated to 800ºC)
Pigment samples were provided by jibanez@geo3bcn.csic.es
defaultW <- getOption("warn")
options(warn=-1)
#Paths
RSpectDir <- "/home/alobo/owncloudRSpect/RSpect"
datadir <- "/home/alobo/WORK/HSI_Lab/Tests_Hypercams/SpecimIQ/SpecimIQTest_2018-09-18/2018-10-18_008"
imadir <- file.path(datadir,"results")
vecdir <- file.path(datadir,"QGIS/Polygons")
#libraries
library(reshape2, quietly = TRUE)
library(plyr, quietly = TRUE)
library(ggplot2, quietly = TRUE)
library(plotly, quietly = TRUE)
library(terra, quietly = TRUE)
library(tidyterra, quietly = TRUE)
library(gridExtra, quietly = TRUE)
library(directlabels, quietly = TRUE)
#Required information
load(file.path(RSpectDir,"IQbands.rda")) #Bands Specim IQ system
1. Read images and polygons
1.1 RGB image
A conventional RGB image in png format is automatically recorded by a
dedicated CCD in Specim IQ. By some reason, this image is rotated
vs. the hyperspectral one, so the code rotates it.
options(warn=-1)
r <- rast(file.path(imadir,"RGBBACKGROUND_2018-10-18_008.png"))
r
class : SpatRaster
dimensions : 645, 645, 4 (nrow, ncol, nlyr)
resolution : 1, 1 (x, y)
extent : 0, 645, 0, 645 (xmin, xmax, ymin, ymax)
coord. ref. :
source : RGBBACKGROUND_2018-10-18_008.png
names : RGBBACK~8_008_1, RGBBACK~8_008_2, RGBBACK~8_008_3, RGBBACK~8_008_4
#plotRGB(r, stretch="lin")
plotRGB(flip(t(r)))

1.2 Reflectance image
The reflectance image was calculated by the Specim IQ system itself
using the average value of the White reference
class : SpatRaster
dimensions : 512, 512, 204 (nrow, ncol, nlyr)
resolution : 1, 1 (x, y)
extent : 0, 512, 0, 512 (xmin, xmax, ymin, ymax)
coord. ref. :
source : REFLECTANCE_2018-10-18_008.dat
names : 397.32, 400.20, 403.09, 405.97, 408.85, 411.74, ...
We display both RGB and CIR color composites with the following
spectral bands:
RGB
| 70 |
Band070 |
598.60 |
| 53 |
Band053 |
548.55 |
| 19 |
Band019 |
449.35 |
CIR
| 203 |
Band203 |
1000.49 |
| 70 |
Band070 |
598.60 |
| 53 |
Band053 |
548.55 |
crs(s) <- ""
#suppressMessages(
ggRad <- ggplot() +
geom_spatraster_rgb(data = stretch(subset(s, subset=defbands), minq=0.02, maxq=0.85)) +
coord_fixed(ratio = 1) +
theme_void() + theme(plot.title = element_text(hjust = 0.5)) +
ggtitle(paste0("\nBands ", paste(defbands,collapse=", ")))
ggRad2 <- ggplot() +
geom_spatraster_rgb(data = stretch(subset(s, subset=defbands2), minq=0.02, maxq=0.85)) +
coord_fixed(ratio = 1) +
theme_void() + theme(plot.title = element_text(hjust = 0.5)) +
ggtitle(paste0("\nBands ", paste(defbands2,collapse=", ")))
#)
grid.arrange(ggRad, ggRad2, ncol=2)

1.3 Polygon vector
Relevant parts of each object were interactively digitized as
polygons in QGIS and used to select spectra from the image.
options(warn=-1)
nompoly <- "008.shp"
p <- vect(file.path(vecdir,nompoly))
#values(p)$Name <- c("White", "Ochre", "Ochre_800",
# "Red", "Red_800",
# "GreenTerra", "GreenTerra_800")
p
class : SpatVector
geometry : polygons
dimensions : 7, 7 (geometries, attributes)
extent : 72.29907, 382.5495, -352.3252, -180.9495 (xmin, xmax, ymin, ymax)
source : 008.shp
coord. ref. : ETRS89 / UTM zone 31N (EPSG:25831)
#knitr::kable(p)
2. Make consistent geometry
In case the CRS is not defined in the image (and this is the
unfortunate case of Specim hdr files), R and QGIS assume, by default,
different geometry for the same tiff. As a consequence, polygons
digitized in QGIS do not overlay the raster image in R. The code must
account for this fact, making a consistent geometry for raster and
vector layer, which we confirm in the RGB display with bands 70 (598
nm), 53 (548 nm) and 19 (449 nm).
crs(s) <- crs(p)
ext(s) <- c(0,512,-512,0)
#plotRGB(stretch(s[[defbands2]], smin=0.0, smax=0.85), ext=c(50,400,-360,-100))
plotRGB(stretch(s[[defbands]], smin=0.0, smax=0.4), ext=c(50,200,-400,-150),
main=IQbands[defbands,])
lines(p, xlim=c(50,200))
text(p,5,cex=0.9,font=2)
text(p,5,cex=0.9,font=1,col=0)

#lines(p,ext=c(50,400,-360,-100)) # "ext" is not a graphical parameter
#lines(ext(c(50,400,-360,-100)))
3. Object spectra
Finally, using the polygons as templates, we extract the reflectance
values from the hyperspectral image and plot.
options(warn=-1)
refldat.m <- terra::extract(s, p, fun=mean, na.rm=TRUE)
refldat.sd <- terra::extract(s, p, fun=sd, na.rm=TRUE)
refldatm <- as.data.frame(t(refldat.m[,-1]))
#colnames(refldatm) <- values(p)$Name
colnames(refldatm) <- values(p)$Acronym
refldatm$Band <- IQbands$Band
refldatm$Wavelength <- IQbands$Wavelength
#head(refldatm)
refldatsd <- as.data.frame(t(refldat.sd[,-1]))
colnames(refldatsd) <- values(p)$Name
#head(refldatsd)
refldat <- melt(refldatm,id.vars = c("Band", "Wavelength"))
#head(refldat)
names(refldat)[3:4] <- c("Sample", "Reflectance")
a <- melt(refldatsd,id.vars=NULL)
#head(a)
refldat$sd <- a$value
refldat$Treatment <- mapvalues(refldat$Sample, from=values(p)$Acronym, to=values(p)$Treatment)
refldat$Pigment <- mapvalues(refldat$Sample, from=values(p)$Acronym, to=values(p)$Pigment)
#head(refldat)
#gg1 <- plothcavspect(datainRef,title="")
#Default colors
ggplotColours <- function(n = 6, h = c(0, 360) + 15){
if ((diff(h) %% 360) < 1) h[2] <- h[2] - 360/n
hcl(h = (seq(h[1], h[2], length = n)), c = 100, l = 65)
}
micolor <- ggplotColours(n=36)
#scales:::show_col(micolor)
#https://www.nceas.ucsb.edu/sites/default/files/2020-04/colorPaletteCheatsheet.pdf
micolor <- c("W"="grey70",
"Och"="coral4",
"Och_800"="chocolate4",
"R"="red",
"R_800"="red4",
"G"="olivedrab3",
"G_800"="olivedrab")
gg1 <- ggplot(data=refldat) +
geom_point(aes(x=Wavelength, y=Reflectance, color=Sample, shape=Treatment),size=0.5) +
geom_line(aes(x=Wavelength, y=Reflectance, color=Sample, linetype=Treatment)) +
geom_errorbar(aes(x=Wavelength,ymin=Reflectance-sd, ymax=Reflectance+sd, color=Sample),
width=.001, alpha=0.5,
position=position_dodge(0.05)) +
xlab("Wavelength (nm)") +
theme(legend.position = "bottom") +
ggtitle("Art pigments")
gg1 + facet_wrap(~Pigment) + scale_color_manual(values=micolor) + geom_dl(aes(x=Wavelength, y=Reflectance, label = Sample), method = list(dl.combine("first.points", "last.points"), cex = 0.7)) + xlim(c(300, 1100))

#gg1 + xlim(c(450, 1000)) +
# facet_wrap(~State) + geom_dl(aes(x=Wavelength, y=Reflectance, label = Sample), method = list(dl.combine("first.points", "last.points"), cex = 0.7))
#gg2 <- gg1 +
ggplotly(gg1,
tooltip=c("Band","Wavelength",Reflectance="Reflectance","Sample"),
height=600, width=900)
NA
LS0tCnRpdGxlOiAiU3BlY2ltIElRIHRlc3Qgd2l0aCBhcnQgcGlnbWVudHMgKDIwMTgwOTE4KSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGZpZ19jYXB0aW9uOiBUUlVFCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KICAudGFibGUge3dpZHRoOiAyNSU7fQo8L3N0eWxlPgoKKiBBZ3VzdGluLkxvYm9AZ2VvM2Jjbi5jc2ljLmVzCiogMjAyMjExMDgKClRoaXMgc2NyaXB0IGV4dHJhY3RzIHJlZmxlY3RhbmNlIHNwZWN0cmEgb2YgYSBzZXQgb2YgYXJ0IHBpZ21lbnRzIGZyb20gYSBWSVNOSVIgaHlwZXJzcGVjdHJhbCBpbWFnZSBhY3F1aXJlZCB3aXRoIGEgZGVtbyB1bml0IG9mIHRoZSBTcGVjaW0gSVEgc3lzdGVtIGluIEhTSUxhYiBwcmVtaXNlcyBvbiAyMDE4MDkxODoKCiogU3BlY2ltIElRIHdoaXRlIHJlZmVyZW5jZSBjYXJkLgoqIE9jcmUgKE9jaHJlKQoqIE9jcmUgODAwIChPY2hyZSBoZWF0ZWQgdG8gODAwwrpDKQoqIFZlcm1lbGwgKElyb24gT3hpZGUgUmVkKQoqIFJlZCA4MDAgKElyb24gT3hpZGUgUmVkIGhlYXRlZCB0byA4MDDCukMpCiogVGVycmEgIChncmVlbiBlYXJ0aCkKKiBUZXJyYSBWZXJkYSA4MDAgKGdyZWVuIGVhcnRoIGhlYXRlZCB0byA4MDDCukMpCgpQaWdtZW50IHNhbXBsZXMgd2VyZSBwcm92aWRlZCBieSBqaWJhbmV6QGdlbzNiY24uY3NpYy5lcwoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRlZmF1bHRXIDwtIGdldE9wdGlvbigid2FybiIpCm9wdGlvbnMod2Fybj0tMSkKI1BhdGhzClJTcGVjdERpciAgICAgICA8LSAiL2hvbWUvYWxvYm8vb3duY2xvdWRSU3BlY3QvUlNwZWN0IgpkYXRhZGlyICAgICAgICAgPC0gIi9ob21lL2Fsb2JvL1dPUksvSFNJX0xhYi9UZXN0c19IeXBlcmNhbXMvU3BlY2ltSVEvU3BlY2ltSVFUZXN0XzIwMTgtMDktMTgvMjAxOC0xMC0xOF8wMDgiCmltYWRpciAgICAgICAgICA8LSBmaWxlLnBhdGgoZGF0YWRpciwicmVzdWx0cyIpCnZlY2RpciAgICAgICAgICA8LSBmaWxlLnBhdGgoZGF0YWRpciwiUUdJUy9Qb2x5Z29ucyIpCiNsaWJyYXJpZXMKbGlicmFyeShyZXNoYXBlMiwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkocGx5ciwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkoZ2dwbG90MiwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkocGxvdGx5LCBxdWlldGx5ID0gVFJVRSkKbGlicmFyeSh0ZXJyYSwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkodGlkeXRlcnJhLCBxdWlldGx5ID0gVFJVRSkKbGlicmFyeShncmlkRXh0cmEsIHF1aWV0bHkgPSBUUlVFKQpsaWJyYXJ5KGRpcmVjdGxhYmVscywgcXVpZXRseSA9IFRSVUUpCgojUmVxdWlyZWQgaW5mb3JtYXRpb24KbG9hZChmaWxlLnBhdGgoUlNwZWN0RGlyLCJJUWJhbmRzLnJkYSIpKSAjQmFuZHMgU3BlY2ltIElRIHN5c3RlbQpgYGAKCiMjIDEuIFJlYWQgaW1hZ2VzIGFuZCBwb2x5Z29ucwojIyMgMS4xIFJHQiBpbWFnZQpBIGNvbnZlbnRpb25hbCBSR0IgaW1hZ2UgaW4gcG5nIGZvcm1hdCBpcyBhdXRvbWF0aWNhbGx5IHJlY29yZGVkIGJ5IGEgZGVkaWNhdGVkIENDRCBpbiBTcGVjaW0gSVEuIEJ5IHNvbWUgcmVhc29uLCB0aGlzIGltYWdlIGlzIHJvdGF0ZWQgdnMuIHRoZSBoeXBlcnNwZWN0cmFsIG9uZSwgc28gdGhlIGNvZGUgcm90YXRlcyBpdC4KYGBge3IgfQpvcHRpb25zKHdhcm49LTEpCnIgPC0gcmFzdChmaWxlLnBhdGgoaW1hZGlyLCJSR0JCQUNLR1JPVU5EXzIwMTgtMTAtMThfMDA4LnBuZyIpKQpyCiNwbG90UkdCKHIsIHN0cmV0Y2g9ImxpbiIpCnBsb3RSR0IoZmxpcCh0KHIpKSkKYGBgCgojIyMgMS4yIFJlZmxlY3RhbmNlIGltYWdlClRoZSByZWZsZWN0YW5jZSBpbWFnZSB3YXMgY2FsY3VsYXRlZCBieSB0aGUgU3BlY2ltIElRIHN5c3RlbSBpdHNlbGYgdXNpbmcgdGhlIGF2ZXJhZ2UgdmFsdWUgb2YgdGhlIFdoaXRlIHJlZmVyZW5jZSAKYGBge3IgZWNobz1PRkZ9Cm9wdGlvbnMod2Fybj0tMSkKbm9tcmVmIDwtIGxpc3QuZmlsZXMoaW1hZGlyLHBhdHQ9J2RhdCcpWzFdCnMgPC0gcmFzdChmaWxlLnBhdGgoaW1hZGlyLG5vbXJlZikpCnMKYGBgCldlIGRpc3BsYXkgYm90aCBSR0IgYW5kIENJUiBjb2xvciBjb21wb3NpdGVzIHdpdGggdGhlIGZvbGxvd2luZyBzcGVjdHJhbCBiYW5kczoKYGBge3IgZWNobz1PRkYsIHJlc3VsdHM9ImFzaXMifQogIGRlZmJhbmRzIDwtIGMoNzAsNTMsMTkpCiAgZGVmYmFuZHMyIDwtIGMoMjAzLDcwLDUzKQogIGNhdCgiUkdCIikKICBrbml0cjo6a2FibGUoSVFiYW5kc1tkZWZiYW5kcyxdKQogIGNhdCgiQ0lSIikKICBrbml0cjo6a2FibGUoSVFiYW5kc1tkZWZiYW5kczIsXSkKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQogIGNycyhzKSA8LSAiIgogICNzdXBwcmVzc01lc3NhZ2VzKAogIGdnUmFkIDwtIGdncGxvdCgpICsgCiAgZ2VvbV9zcGF0cmFzdGVyX3JnYihkYXRhID0gc3RyZXRjaChzdWJzZXQocywgc3Vic2V0PWRlZmJhbmRzKSwgbWlucT0wLjAyLCBtYXhxPTAuODUpKSArCiAgICBjb29yZF9maXhlZChyYXRpbyA9IDEpICsKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgICBnZ3RpdGxlKHBhc3RlMCgiXG5CYW5kcyAiLCBwYXN0ZShkZWZiYW5kcyxjb2xsYXBzZT0iLCAiKSkpCiAgZ2dSYWQyIDwtIGdncGxvdCgpICsgCiAgICBnZW9tX3NwYXRyYXN0ZXJfcmdiKGRhdGEgPSBzdHJldGNoKHN1YnNldChzLCBzdWJzZXQ9ZGVmYmFuZHMyKSwgbWlucT0wLjAyLCBtYXhxPTAuODUpKSArCiAgICBjb29yZF9maXhlZChyYXRpbyA9IDEpICsKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgICBnZ3RpdGxlKHBhc3RlMCgiXG5CYW5kcyAiLCBwYXN0ZShkZWZiYW5kczIsY29sbGFwc2U9IiwgIikpKQogICMpCiAgZ3JpZC5hcnJhbmdlKGdnUmFkLCBnZ1JhZDIsIG5jb2w9MikKYGBgCgojIyMgMS4zIFBvbHlnb24gdmVjdG9yClJlbGV2YW50IHBhcnRzIG9mIGVhY2ggb2JqZWN0IHdlcmUgaW50ZXJhY3RpdmVseSBkaWdpdGl6ZWQgYXMgcG9seWdvbnMgaW4gUUdJUyBhbmQgdXNlZCB0byBzZWxlY3Qgc3BlY3RyYSBmcm9tIHRoZSBpbWFnZS4KYGBge3IgfQpvcHRpb25zKHdhcm49LTEpCm5vbXBvbHkgPC0gIjAwOC5zaHAiCnAgPC0gdmVjdChmaWxlLnBhdGgodmVjZGlyLG5vbXBvbHkpKQojdmFsdWVzKHApJE5hbWUgPC0gYygiV2hpdGUiLCAiT2NocmUiLCAiT2NocmVfODAwIiwKIyAgICAgICAgICAgICAgICAgICAgIlJlZCIsICJSZWRfODAwIiwKIyAgICAgICAgICAgICAgICAgICAgIkdyZWVuVGVycmEiLCAiR3JlZW5UZXJyYV84MDAiKQpwCiNrbml0cjo6a2FibGUocCkKYGBgCgojIyAyLiBNYWtlIGNvbnNpc3RlbnQgZ2VvbWV0cnkKSW4gY2FzZSB0aGUgQ1JTIGlzIG5vdCBkZWZpbmVkIGluIHRoZSBpbWFnZSAoYW5kIHRoaXMgaXMgdGhlIHVuZm9ydHVuYXRlIGNhc2Ugb2YgU3BlY2ltIGhkciBmaWxlcyksIFIgYW5kIFFHSVMgYXNzdW1lLCBieSBkZWZhdWx0LCBkaWZmZXJlbnQgZ2VvbWV0cnkgZm9yIHRoZSBzYW1lIHRpZmYuIEFzIGEgY29uc2VxdWVuY2UsIHBvbHlnb25zIGRpZ2l0aXplZCBpbiBRR0lTIGRvIG5vdCBvdmVybGF5IHRoZSByYXN0ZXIgaW1hZ2UgaW4gUi4KVGhlIGNvZGUgbXVzdCBhY2NvdW50IGZvciB0aGlzIGZhY3QsIG1ha2luZyBhIGNvbnNpc3RlbnQgZ2VvbWV0cnkgZm9yIHJhc3RlciBhbmQgdmVjdG9yIGxheWVyLCB3aGljaCB3ZSBjb25maXJtIGluIHRoZSBSR0IgZGlzcGxheSB3aXRoIGJhbmRzIDcwICg1OTggbm0pLCA1MyAoNTQ4IG5tKSBhbmQgMTkgKDQ0OSBubSkuCgpgYGB7ciB9CmNycyhzKSA8LSBjcnMocCkKZXh0KHMpIDwtIGMoMCw1MTIsLTUxMiwwKQojcGxvdFJHQihzdHJldGNoKHNbW2RlZmJhbmRzMl1dLCBzbWluPTAuMCwgc21heD0wLjg1KSwgZXh0PWMoNTAsNDAwLC0zNjAsLTEwMCkpCnBsb3RSR0Ioc3RyZXRjaChzW1tkZWZiYW5kc11dLCBzbWluPTAuMCwgc21heD0wLjQpLCBleHQ9Yyg1MCwyMDAsLTQwMCwtMTUwKSwKICAgICAgICBtYWluPUlRYmFuZHNbZGVmYmFuZHMsXSkKbGluZXMocCwgeGxpbT1jKDUwLDIwMCkpCnRleHQocCw1LGNleD0wLjksZm9udD0yKQp0ZXh0KHAsNSxjZXg9MC45LGZvbnQ9MSxjb2w9MCkKI2xpbmVzKHAsZXh0PWMoNTAsNDAwLC0zNjAsLTEwMCkpICMgImV4dCIgaXMgbm90IGEgZ3JhcGhpY2FsIHBhcmFtZXRlcgojbGluZXMoZXh0KGMoNTAsNDAwLC0zNjAsLTEwMCkpKQpgYGAKCiMjIDMuIE9iamVjdCBzcGVjdHJhCkZpbmFsbHksIHVzaW5nIHRoZSBwb2x5Z29ucyBhcyB0ZW1wbGF0ZXMsIHdlIGV4dHJhY3QgdGhlIHJlZmxlY3RhbmNlIHZhbHVlcyBmcm9tIHRoZSBoeXBlcnNwZWN0cmFsIGltYWdlIGFuZCBwbG90LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQpvcHRpb25zKHdhcm49LTEpCnJlZmxkYXQubSAgPC0gdGVycmE6OmV4dHJhY3QocywgcCwgZnVuPW1lYW4sIG5hLnJtPVRSVUUpCnJlZmxkYXQuc2QgPC0gdGVycmE6OmV4dHJhY3QocywgcCwgZnVuPXNkLCBuYS5ybT1UUlVFKQoKcmVmbGRhdG0gPC0gYXMuZGF0YS5mcmFtZSh0KHJlZmxkYXQubVssLTFdKSkKI2NvbG5hbWVzKHJlZmxkYXRtKSA8LSB2YWx1ZXMocCkkTmFtZQpjb2xuYW1lcyhyZWZsZGF0bSkgPC0gdmFsdWVzKHApJEFjcm9ueW0KcmVmbGRhdG0kQmFuZCA8LSBJUWJhbmRzJEJhbmQKcmVmbGRhdG0kV2F2ZWxlbmd0aCA8LSBJUWJhbmRzJFdhdmVsZW5ndGgKI2hlYWQocmVmbGRhdG0pCnJlZmxkYXRzZCA8LSBhcy5kYXRhLmZyYW1lKHQocmVmbGRhdC5zZFssLTFdKSkKY29sbmFtZXMocmVmbGRhdHNkKSA8LSB2YWx1ZXMocCkkTmFtZQojaGVhZChyZWZsZGF0c2QpCgpyZWZsZGF0IDwtIG1lbHQocmVmbGRhdG0saWQudmFycyA9IGMoIkJhbmQiLCAiV2F2ZWxlbmd0aCIpKQojaGVhZChyZWZsZGF0KQpuYW1lcyhyZWZsZGF0KVszOjRdIDwtIGMoIlNhbXBsZSIsICJSZWZsZWN0YW5jZSIpCgphIDwtIG1lbHQocmVmbGRhdHNkLGlkLnZhcnM9TlVMTCkKI2hlYWQoYSkKcmVmbGRhdCRzZCA8LSBhJHZhbHVlCnJlZmxkYXQkVHJlYXRtZW50IDwtIG1hcHZhbHVlcyhyZWZsZGF0JFNhbXBsZSwgZnJvbT12YWx1ZXMocCkkQWNyb255bSwgdG89dmFsdWVzKHApJFRyZWF0bWVudCkKcmVmbGRhdCRQaWdtZW50IDwtIG1hcHZhbHVlcyhyZWZsZGF0JFNhbXBsZSwgZnJvbT12YWx1ZXMocCkkQWNyb255bSwgdG89dmFsdWVzKHApJFBpZ21lbnQpCiNoZWFkKHJlZmxkYXQpCgojZ2cxIDwtIHBsb3RoY2F2c3BlY3QoZGF0YWluUmVmLHRpdGxlPSIiKQojRGVmYXVsdCBjb2xvcnMKZ2dwbG90Q29sb3VycyA8LSBmdW5jdGlvbihuID0gNiwgaCA9IGMoMCwgMzYwKSArIDE1KXsKICBpZiAoKGRpZmYoaCkgJSUgMzYwKSA8IDEpIGhbMl0gPC0gaFsyXSAtIDM2MC9uCiAgaGNsKGggPSAoc2VxKGhbMV0sIGhbMl0sIGxlbmd0aCA9IG4pKSwgYyA9IDEwMCwgbCA9IDY1KQp9CgptaWNvbG9yIDwtIGdncGxvdENvbG91cnMobj0zNikKI3NjYWxlczo6OnNob3dfY29sKG1pY29sb3IpCiNodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9zaXRlcy9kZWZhdWx0L2ZpbGVzLzIwMjAtMDQvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYKbWljb2xvciA8LSBjKCJXIj0iZ3JleTcwIiwgCiAgICAgICAgICAgICAiT2NoIj0iY29yYWw0IiwKICAgICAgICAgICAgICJPY2hfODAwIj0iY2hvY29sYXRlNCIsCiAgICAgICAgICAgICAiUiI9InJlZCIsCiAgICAgICAgICAgICAiUl84MDAiPSJyZWQ0IiwKICAgICAgICAgICAgICJHIj0ib2xpdmVkcmFiMyIsCiAgICAgICAgICAgICAiR184MDAiPSJvbGl2ZWRyYWIiKQoKZ2cxIDwtIGdncGxvdChkYXRhPXJlZmxkYXQpICsKICBnZW9tX3BvaW50KGFlcyh4PVdhdmVsZW5ndGgsIHk9UmVmbGVjdGFuY2UsIGNvbG9yPVNhbXBsZSwgc2hhcGU9VHJlYXRtZW50KSxzaXplPTAuNSkgKwogIGdlb21fbGluZShhZXMoeD1XYXZlbGVuZ3RoLCB5PVJlZmxlY3RhbmNlLCBjb2xvcj1TYW1wbGUsIGxpbmV0eXBlPVRyZWF0bWVudCkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh4PVdhdmVsZW5ndGgseW1pbj1SZWZsZWN0YW5jZS1zZCwgeW1heD1SZWZsZWN0YW5jZStzZCwgY29sb3I9U2FtcGxlKSwKICAgICAgICAgICAgICAgIHdpZHRoPS4wMDEsIGFscGhhPTAuNSwKICAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMDUpKSArCiAgeGxhYigiV2F2ZWxlbmd0aCAobm0pIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ2d0aXRsZSgiQXJ0IHBpZ21lbnRzIikKCiAgZ2cxICsgZmFjZXRfd3JhcCh+UGlnbWVudCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPW1pY29sb3IpICsgZ2VvbV9kbChhZXMoeD1XYXZlbGVuZ3RoLCB5PVJlZmxlY3RhbmNlLCBsYWJlbCA9IFNhbXBsZSksIG1ldGhvZCA9IGxpc3QoZGwuY29tYmluZSgiZmlyc3QucG9pbnRzIiwgImxhc3QucG9pbnRzIiksIGNleCA9IDAuNykpICsgeGxpbShjKDMwMCwgMTEwMCkpCgojZ2cxICsgeGxpbShjKDQ1MCwgMTAwMCkpICArIAojICBmYWNldF93cmFwKH5TdGF0ZSkgKyBnZW9tX2RsKGFlcyh4PVdhdmVsZW5ndGgsIHk9UmVmbGVjdGFuY2UsIGxhYmVsID0gU2FtcGxlKSwgbWV0aG9kID0gbGlzdChkbC5jb21iaW5lKCJmaXJzdC5wb2ludHMiLCAibGFzdC5wb2ludHMiKSwgY2V4ID0gMC43KSkgCiNnZzIgPC0gZ2cxICsgCgpnZ3Bsb3RseShnZzEsCiAgICAgICAgIHRvb2x0aXA9YygiQmFuZCIsIldhdmVsZW5ndGgiLFJlZmxlY3RhbmNlPSJSZWZsZWN0YW5jZSIsIlNhbXBsZSIpLAogICAgICAgICBoZWlnaHQ9NjAwLCB3aWR0aD05MDApCgpgYGAKCg==