簡體   English   中英

如何通過R中的少量旋轉位圖

[英]How to Rotate a Bitmap by Small Amount in R

問題:

這與圖像數據的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張rotateImage圖像,使用原始的rotateImage根據10次旋轉生成了11倍的rotateImage方法...並在6分鍾內完成,包括所有對非SSD磁盤的讀取/寫入。 我仍然不確定為什么我不能讓R以大致相同的效率翻閱所有內容。

這演示了處理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) )

在此處輸入圖片說明

另一種方法是使用Bioconductor軟件包EBImage提供的功能, EBImageR的圖像處理和分析工具箱。 要安裝軟件包,請使用:

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)

img

注意縮放比例為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 ,否則將擴展生成的圖像,以使旋轉后的圖像適合其中。

可以在函數getFrames的幫助下將像素數組展平為data.frame ,該函數返回圖像幀列表。 這些可以擴展為向量,並逐行組合。 原始數字名稱將作為結果數據幀的第一列。

dfr = as.data.frame(cbind(as.numeric(dimnames(imgr)[[3]]), 
                          do.call(rbind, lapply(getFrames(imgr), as.vector))))

使用通用仿射變換執行旋轉,該仿射變換在C中有效實現。它使用雙線性濾波,可以通過將filter = "none"設置為關閉。 根據我的微基准測量, rotate功能比快18倍左右rotateImageM功能(和200X速度相比原來的rotateImage功能)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM