问题:
这与图像数据的MNIST呈现有关。我想对图像进行精细旋转。所有图像的集合表示为data.frame,其中每行(每个图像)是785个字符向量;第一个字符是数字的值;剩下的784是一个扁平的28x28像素矩阵。
我想有效地旋转784个字符以生成一些额外的旋转图像;即我想设计一些新数据!
我已经使用以下代码完成了此操作,但我现在要做的是提高效率。目前,使用2.7GHz i7机器上的6个线程,在42,000张图像上运行需要数小时。
####################################################################################################
# rotateImage() -- generates new images rotated from -angleDegree tp +angleDegree
# images will be returned as a data.frame
####################################################################################################
xOffset = 14
yOffset = 14
rotateImage <- function(imgData,angleDegree) {
for (t in -angleDegree:angleDegree) {
newImgData <- t(as.data.frame(rep(0,785)))
newImgData[1,1]=imgData[1,1] # set the actual digit value to imgData value
for (j in 1:28) {
for (i in 1:28) {
newI = round(xOffset + (((i-xOffset)*cos(t*pi/180)) + ((j-yOffset)*sin(t*pi/180))))
newJ = round(yOffset + (((xOffset-i)*sin(t*pi/180)) + ((j-yOffset)*cos(t*pi/180))))
if ((newI %in% 1:28) && (newJ %in% 1:28)) {
newImgData[1,1 + newI + ((newJ-1)*28)] = imgData[1,1+i+((j-1)*28)]
}
}
}
if (exists("retImages")) {
retImages <- rbind(retImages,newImgData)
} else {
retImages = newImgData
}
}
retImages
}
####################################################################################################
# various globals
degreesToRotate = 5
# one sample image
sampleData = t(as.data.frame(c(8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,34,0,0,0,57,136,162,245,203,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,147,249,253,224,232,232,6,81,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,91,254,253,242,128,17,97,240,149,254,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,253,254,151,38,0,0,47,253,253,228,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,221,254,160,0,0,0,0,164,254,228,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,253,253,9,0,0,17,130,251,223,73,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,253,253,17,0,19,199,254,223,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,186,253,235,101,199,253,195,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,210,255,254,254,228,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,254,253,253,211,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,149,254,215,232,253,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,253,228,15,107,253,184,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,254,228,0,0,17,235,229,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,243,164,15,0,0,13,222,241,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,119,254,206,9,0,0,0,141,253,142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,234,228,40,0,0,0,102,240,219,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,254,102,0,0,0,128,245,188,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,168,0,9,89,172,254,160,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,169,83,174,242,230,80,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,253,228,143,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)))
# image rotated from -5 to 5 degrees:
rotations <- rotateImage(sampleData,degreesToRotate)
# plot the images to verify correctness
library(raster)
bw=grey.colors(2,start=0,end=1)
for (i in 1:nrow(rotations)) {
pixData <- rotations[i,2:785]
dim(pixData) <- c(28,28)
image(pixData, col=bw)
}
基本上这是从关闭所有像素开始的(所以剪切不是麻烦 - 我正在做精细旋转,而不是45度的东西),并且在两个循环中,任何“开”像素将被移动到新的位置对于旋转的图像。 (顺便说一句,这是在原始图像的同一“帧”内在R中进行精细角度图像旋转的简单方法。)
我想用一些运行得更快的花哨的单线转换来消除i和j循环。任何有关这些方面的提示或其他新颖的想法都将不胜感激。
解决方案:
一个已接受的答案(参见下面的@ 42-),利用TsparseMatrix;并在此处略微修改为函数:
####################################################################################################
xOffsetM = 13
yOffsetM = 13
rotateImageM <- function(imgData,angleDegree) {
for (t in -angleDegree:angleDegree) {
rotMat <- matrix( c( cos(t*pi/180), sin(t*pi/180), -sin(t*pi/180), cos(t*pi/180)), 2, 2)
M <- as(matrix(imgData[,2:785], 28, 28), "TsparseMatrix")
idxs <- round(cbind(M@i - xOffsetM, M@j - yOffsetM) %*% rotMat)
M@i <- as.integer(idxs[,1]+xOffsetM) # back shift to right and ..
M@j <- as.integer(idxs[,2]+yOffsetM) # back "up"
newImgData <- imgData
newImgData[,2:785] <- t(as.data.frame(as.vector(M)))
if (exists("retImages")) {
retImages <- rbind(retImages,newImgData)
} else {
retImages <- newImgData
}
}
retImages
}
对于其他对这类问题感到好奇的人,你可能会看到图像尺寸(28x28)是任意的 - 特定于我正在研究的例子;这些也决定了解决方案中“偏移”选择为14或13。因此,这应该很容易扩展到任何大小的图像。
在一天结束时......
所提出的解决方案都有效,但是对于大量图像来说仍然有点慢或内存效率低下。我最终在VB.Net中编写了一个简单的应用程序(我很清楚,没有其他理由与其他任何东西相比),它处理了42,000个图像,使用原始版本生成11次基于10次旋转的结果{1}}接近...并在6分钟内完成,包括对非SSD磁盘的所有读/写。我仍然不确定为什么我不能让R以大致相同的效率翻转所有内容。
答案 0 :(得分:1)
这展示了处理sparseMatrix表示的策略:
M <- as( matrix( sampleData[-1], 28), "TsparseMatrix")
idxs <- floor( cbind( M@i-13, M@j-13) %*%
# This implicitly shifts the rows down and columns left by 14 since sparse i-j's are 0-based
matrix( c(cos(5*2*pi/360), sin(5*2*pi/360),
-sin(15*2*pi/360), cos(5*2*pi/360)), 2,2) ) # 5 degrees
M@i <- as.integer( idxs[,1]+14) # back shift to right and ..
M@j=as.integer( idxs[,2]+14) # back "up"
image( matrix(sampleData[-1], 28) )
image( as.matrix(M) )
答案 1 :(得分:1)
另一种方法是使用Bioconductor软件包提供的功能 EBImage ,这是 R 的图像处理和分析工具箱。要安装软件包,请使用:
source("http://bioconductor.org/biocLite.R")
biocLite("EBImage")
然后,您可以将图像数据表示为Image
对象,并使用rotate
函数对其进行转换。这由以下代码说明。
如果原始像素数据存储在数据框中,则首先需要将其转换为 EBImage 使用的基于数组的格式。
library("EBImage")
# sample data.frame containg 3 copies of sampleData
df = rbind(sampleData, sampleData, sampleData)
# convert rows to 28x28 matrices and combine them into a single image stack
img = combine(lapply(1:nrow(df), function(i) Image(df[i, -1], c(28, 28))))
# set frame names to digit values
dimnames(img) = list(NULL, NULL, df[, 1])
# img contains an array of 3 grayscale 28x28 frames
img
## Image
## colorMode : Grayscale
## storage.mode : double
## dim : 28 28 3
## frames.total : 3
## frames.render: 3
##
## imageData(object)[1:5,1:6,1]
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0 0 0 0 0 0
## [2,] 0 0 0 0 0 0
## [3,] 0 0 0 0 0 0
## [4,] 0 0 0 0 0 0
## [5,] 0 0 0 0 0 0
# display the first frame
display(getFrame(img, 1)/255, method = "raster", interpolate = FALSE)
请注意缩放1/255,这是因为EBImage使用[0:1]范围内的强度来绘制图像数据。
一旦您的数据格式正确,所有帧都可以使用函数rotate
一次旋转。
# rotate of the whole image stack
imgr = rotate(img, angle = 5, output.dim = dim(img)[1:2])
display(getFrame(imgr, 1)/255, method = "raster", interpolate = FALSE)
要保留原始图片尺寸,我们需要设置output.dim
,否则生成的图像会被展开,以便旋转的图像适合它。
在函数data.frame
的帮助下,像素阵列可以平展回getFrames
,函数返回图像帧列表。这些可以扩展到向量并逐行组合。原始数字名称前置为结果数据框的第一列。
dfr = as.data.frame(cbind(as.numeric(dimnames(imgr)[[3]]),
do.call(rbind, lapply(getFrames(imgr), as.vector))))
使用在C中有效实现的通用affine transformation执行旋转。它使用双线性过滤,可以通过设置filter = "none"
来关闭。根据我的microbenchmark测量结果,rotate
函数比rotateImageM
函数快约18倍(与原始rotateImage
函数相比快了200倍)。