我为世界上每个国家制作了一系列图像,其中像素颜色表示特定的植被覆盖。原始植被栅格来自GLC2000 project。来自Natural Earth的国家/地区边界。
我尝试使用ImageMagick(在Debian中)计算每个图像的像素:
for map in *.png; do convert $map -format %c histogram:info:hist/$map.txt; done
但是存在一个问题:高纬度地区的像素变形,因此应该乘以纬度的余弦,因此它们的实际面积将被计算。我喜欢ImageMagick,因为它比R(我的替代品)要快得多。我可以用它吗?或者还有其他实用的解决方案吗?我现在正在R写一个剧本,但我认为这需要很长时间(235个国家)。
答案 0 :(得分:1)
或者,如果您只有几种颜色,则可以按颜色处理颜色。您可以转换图像,使相关颜色为白色,其余均为黑色。然后平均到一行,使平均值在0到1的范围内(将实际平均值除以颜色的最大值--255)。然后,行的像素的平均值乘以行的宽度给出计数。因此,如果平均为一列并将列值乘以cos(lat),则会通过lat告诉您该颜色的计数。然后你会为下一种颜色做同样的事情。
以下是使用Imagemagick创建的3种颜色的11x11 45度渐变色的示例图像。
convert -size 11x11 -define gradient:angle=45 gradient: -interpolate nearest-neighbor \( -size 1x1 xc:red xc:green1 xc:blue +append \) -clut img.png
我可以通过以下方式逐行获取green1颜色的平均值,范围为0到255:
convert img.png -fill white -opaque green1 -fill black +opaque white +write color.png -scale 1x! -type grayscale txt: | sed -n 's/^.*gray[(]\(.*\)[)]/\1/p'
139
162
185
209
232
255
232
209
185
162
139
这是在缩放到一列以产生平均值之前创建的二进制掩码图像:
然后我可以通过除以255将值更改为0到1的范围,然后乘以宽度以获得计数(bash unix语法):
wd=`convert -ping img.png -format "%w" info:`
echo "$wd"
arr=(`convert img.png -fill white -opaque green1 -fill black +opaque white +write color.png -scale 1x! -type grayscale txt: | sed -n 's/^.*gray[(]\(.*\)[)]/\1/p'`)
num=${#arr[*]}
echo "num=$num"
echo "${arr[*]}"
for ((i=0; i<num; i++)); do
convert xc: -format "%[fx:$wd*${arr[$i]}/255)]\n" info:
done
5.99608
6.98824
7.98039
9.01569
10.0078
11
10.0078
9.01569
7.98039
6.98824
5.99608
如果顶行对应转换为y = 1的值,中心行转换为y = 0的值,底部转换为y = 1的值,则取cos(y * pi / 4)* count应该做你想要的假设中心位于赤道,顶部和底部是纬度45度。我认为以下可能会这样做。
wd=`convert -ping img.png -format "%w" info:`
echo "$wd"
arr=(`convert img.png -fill white -opaque green1 -fill black +opaque white +write color.png -scale 1x! -type grayscale txt: | sed -n 's/^.*gray[(]\(.*\)[)]/\1/p'`)
num=${#arr[*]}
echo "num=$num"
echo "${arr[*]}"
for ((i=0; i<num; i++)); do
convert xc: -format "%[fx:cos((pi/4)*abs(($i-($num-1)/2))/(($num-1)/2)))*$wd*${arr[$i]}/255)]\n" info:
done
4.23987
5.6536
7.11058
8.57443
9.88463
11
9.88463
8.57443
7.11058
5.6536
4.23987
答案 1 :(得分:0)
如果我理解这个问题,对于高纬度地区,您需要拉伸图像的宽度,以便获得更多计数来校正投影类型。也就是说,在图中宽度较高的纬度被压缩。因此,您需要拉伸图像顶部和底部的宽度,以便在获得直方图时,您可以看到更多来自高纬度地区的颜色。如果这是正确的,那么在Imagemagick中,您可以按照以下方式扭曲图像,以根据余弦函数执行此操作。
假设lena图像对应于赤道中心并上下45度(pi / 4弧度)。
convert lena.png -virtual-pixel none -fx "lat=(pi/4)*(j-h/2)/(h/2); u.p{cos(lat)*i,j}" tmp.png
所以这里垂直中心不应该失真,但随着你走得更高或更低,数据会延伸到更多的像素。
这是你需要的吗?
如果你需要保持混合的颜色(由于分数像素插值,然后添加 - 插入最近邻近。
convert lena.png -virtual-pixel none -interpolate nearestneighbor -fx "lat=(pi/4)*(j-h/2)/(h/2); u.p{cos(lat)*i,j}" tmp2.png
答案 2 :(得分:0)
您可以在Imagemagick中提取直方图作为文本。然后脚本将计数提取为数组。然后循环遍历数组并计算cos(lat)* array_value作为数组索引的函数。例如,在这里我制作一个简单的3色图像,每种颜色有10x10个补丁,因此计数应为100,100,100。然后我循环遍历数组并校正纬度,假设图像的中心位于赤道,顶部和底部颜色的纬度为45度。 Unix语法。
arr=(`convert -size 10x10 xc:red xc:green1 xc:blue -format "%c" histogram:info: | sed 's/^[ ]*//' | cut -d: -f1`)
num=${#arr[*]}
echo "num=$num"
echo "${arr[*]}"
for ((i=0; i<num; i++)); do
im6912 convert xc: -format "%[fx:cos((pi/4)*abs($i-($num-1)/2))/(($num-1)/2)))*${arr[$i]}]\n" info:
done
结果是:
70.7107
100
70.7107
sed&#39; s / ^ [] * //&#39;从文本直方图中删除前导空格。 cut -d:-f1只从直方图文本输出的每一行中提取计数。
($ num-1)/ 2是中心线。所以我将从0到2的行索引转换为从-1到1的行索引,其中0是中心。
答案 3 :(得分:0)
我用R来解决它,这很慢(我把它从一天运行到下一天),但至少我更容易理解发生了什么。来自@ fmw42的解决方案可能更快,但我甚至没有尝试过,因为当我看到它时我已经完成了运行。
library(rgdal)
library(raster)
library(png)
setwd('~/R/GLC2000/paises')
pa <- readOGR('../../shp/ne_10m_admin_0_countries','ne_10m_admin_0_countries')
GLCcolors <- c('#000000','#006400','#009600','#afff63','#8b4513','#cd7f60','#8cbe00','#7896ff',
'#0047c8','#00e600','#ff7700','#ffb300','#ffea9e','#decaa1','#009696','#ffe0e5',
'#ff75e8','#ca8aff','#b4b4b4','#8ae3ff','#f0f0f0','#ff0000','#ffffff')
d <- matrix(0,1,24)
d <- as.data.frame(d)
colnames(d) <- c('ISO3',GLCcolors)
for (p in list.files(pattern='*.png')) {
pImg <- readPNG(p)
p <- substr(p,1,3)
pb <- grep(p,pa$ISO_A3)
ext <- extent(pa[pb,])
pI <- nrow(d)+1
d[pI,1] <- p
d[pI,2:24] <- 0
for (h in 1:nrow(pImg)) {
lat <- ext@ymax - h*(ext@ymax-ext@ymin)/nrow(pImg)
co <- cos(pi*lat/180)
for (w in 1:ncol(pImg)) {
pix <- pImg[h,w,]
if (sum(pix) == 0) {
d[pI,2] = d[pI,2] + co
} else {
pixCor <- paste0('#',
format(as.hexmode(pix[1]*255),2),
format(as.hexmode(pix[2]*255),2),
format(as.hexmode(pix[3]*255),2))
if (length(grep(pixCor,colnames(d),fixed=T)) == 1) {
d[pI,grep(pixCor,colnames(d),fixed=T)] = d[pI,grep(pixCor,colnames(d),fixed=T)] + co
} else {
print(paste0('ERROR: color not found = ',pixCor,' (',p,' w=',w,' h=',h,')'))
}
}
}
if (h %% 100 == 0) {
print(paste0(p,' ',h,'/',nrow(pImg)))
flush.console()
}
}
}
write.table(d,'../countries.txt',quote=F,sep='\t',na='',row.names=F)