如何根据高度计算不同乘数的图像像素?

时间:2018-04-19 20:59:28

标签: r pixels imagemagick-convert

我为世界上每个国家制作了一系列图像,其中像素颜色表示特定的植被覆盖。原始植被栅格来自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个国家)。

4 个答案:

答案 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


enter image description here

我可以通过以下方式逐行获取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


这是在缩放到一列以产生平均值之前创建的二进制掩码图像:

enter image description here

然后我可以通过除以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弧度)。

enter image description here

convert lena.png -virtual-pixel none -fx "lat=(pi/4)*(j-h/2)/(h/2); u.p{cos(lat)*i,j}" tmp.png

enter image description here

所以这里垂直中心不应该失真,但随着你走得更高或更低,数据会延伸到更多的像素。

这是你需要的吗?

如果你需要保持混合的颜色(由于分数像素插值,然后添加 - 插入最近邻近。

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

enter image description here

答案 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)