【问题标题】:Read image data into R piece-by-piece将图像数据逐片读入R
【发布时间】:2016-11-16 21:19:30
【问题描述】:

我正在处理一些相当大的图像文件(航测马赛克,通常 > 10 亿像素),因此将整个图像加载到内存中将成为我系统的问题。我想将它们一块一块地放入 R 中,这样我就可以在“网格”部分中处理它们。

注意:我不依赖于特定的图像格式,所以tiffpngbmp 等都可以作为输入。

我可以用readJPEG 做一些类似的事情,但这需要先将整个文件加载到内存中,所以它并不能真正解决我的问题,但希望能展示我想要实现的目标。

image.file <- "~/Desktop/penguins.jpg"

grid.size <- 100
v <- 3
h <- 1

library( jpeg )
image <- readJPEG( image.file )[ seq.int( (v-1)*grid.size+1, (v)*grid.size, 1 ),
                                 seq.int( (h-1)*grid.size+1, h*grid.size, 1 ), ]

上面仅加载图像的一个样本,由grid.sizevh 指定,这样可以很容易地将其构建到循环中以分段分析图像。

是否可以在不将整个图像加载到内存的情况下实现这一点?像read.csv 这样的东西,使用skipn 参数是合理的(它至少一次只加载一个垂直部分,比readJPEG 需要的内存少得多)。

【问题讨论】:

    标签: r image


    【解决方案1】:

    借助 RBioFormats(可从 GitHub 获得),您可以轻松地在 R 中为几乎任何图像格式实现这一点。

    devtools::install_github("aoles/RBioFormats")
    

    可以在read.image()subset 参数中指定块大小。以下示例说明了如何在不将整个文件加载到内存的情况下分段处理图像。

    library(RBioFormats)
    
    filename <- system.file("images", "sample-color.png", package="EBImage")
    
    ## first, get image dimensions from metadata
    meta <- coreMetadata(read.metadata(filename))
    
    xdim <- meta$sizeX
    ydim <- meta$sizeY
    
    ## set chunk size
    chunksize <- 300  
    
    ## itarate over image chunks row-wise
    for(i in 1:ceiling(ydim/chunksize)) {
      for(j in 1:ceiling(xdim/chunksize)) {
        x1 <- (j-1) * chunksize + 1
        x2 <- min( j * chunksize, xdim )
        y1 <- (i-1) * chunksize + 1
        y2 <- min( i * chunksize, ydim )
    
        cat(sprintf("[%d:%d, %d:%d] ", x1, x2, y1, y2))
    
        img <- read.image(filename, subset = list(X=x1:x2, Y=y1:y2))
    
        ## perform the actual image processing
        ## here we just print the min and max pixel intensities
        cat(range(img), "\n")        
      }
    }
    

    您可能还想查看 EBImage,这是 R 的图像处理工具箱。它提供了查看图像和执行各种转换和过滤的功能。

    【讨论】:

    • 感谢您的回复!这看起来是一个很好的解决方案,但它似乎不适用于非常大的图像。我可以为小 jpg 运行 img &lt;-... 行,但是当我尝试传递一个大(8E8 像素)的 jpg 时,我收到错误 Error in .jcall(reader, "I", "getSeriesCount") : java.lang.IllegalArgumentException: Array size &gt; Integer.MAX_VALUE!。也许是尺寸限制?
    • 我明白了 - 您是否可以共享触发错误的特定文件以便我可以调试它?
    • 当然。我刚刚在 DropBox 上与您共享了一个文件,使用的是“EBImage”DESCRIPTION 文件中列出的您的电子邮件。请不要分发该图像,但我很高兴您将其用于调试。
    • 这看起来像是对 java 中整数类的限制(最大超过 2E8)。也许将“I”(github 上 read.image.R 的第 214 行)更改为“J”以使用长值而不是整数?
    • github 似乎是一个更好的讨论场所。我在那里打开了一个问题。
    【解决方案2】:

    如果您安装了 ImageMagick,您可以在将其读入 R 之前crop the image from the command line。使用此图像的示例:http://www.worldatlas.com/worldmaps/worldpoliticallarge.jpg

    创建裁剪图像:

    x <- 800  ## x and y are offsets
    y <- 400
    w <- 200  ## width and height of cropped image
    h <- 100
    filename <- "worldpoliticallarge.jpg"
    outname <- "crop.jpg"
    cmd <- sprintf("jpegtran -crop %dx%d+%d+%d -copy none %s > %s", w, h, x, y, filename, outname)
    system(cmd)
    

    检查新图像是否包含我们想要的区域:

    library(jpeg)
    original <- readJPEG(filename)
    cropped <- readJPEG(outname)
    all.equal(original[(y+1):(y+h), (x+1):(x+w), ], cropped)
    # [1] TRUE
    

    【讨论】:

    • 好主意。当 ImageMagick 进行裁剪时,我将不得不测试内存负载。你知道它是否将整个图像加载到内存中进行裁剪吗?
    • 我做了一些测试,这个解决方案真的很好用!但是,它似乎确实将整个图像加载到内存中(对于 8E8 像素的 jpg 图像,裁剪 200x100 部分,它将 3.4GB 加载到 RAM 中)。不过,它比作为数组加载到 R 中要容易得多,所以我接受了答案。如果有人有内存使用率较低的解决方案(ImageMagick 文档建议它可以做到,但我还不相信),我也会非常感激。非常感谢@WeihuanWong!
    • 在内存消耗方面,基于 RBioFormats 的解决方案似乎更高效:我能够在不超过 0.5GB 的情况下迭代 1000 x 1000 块的图像在任何给定时间的内存使用量。
    • 很高兴认识@aoles。感谢您不厌其烦地进行测试
    猜你喜欢
    • 2015-06-20
    • 2018-08-26
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    • 2018-08-11
    • 2015-07-12
    • 1970-01-01
    相关资源
    最近更新 更多