在最近的一個(gè)項(xiàng)目中,需要實(shí)現(xiàn) Mac OS X 環(huán)境下的攝像頭圖像實(shí)時(shí)捕獲并轉(zhuǎn)換為 java 中的 BufferedImage 對象。首先通過開發(fā)一個(gè)本地庫實(shí)現(xiàn) Mac OS X 的攝像頭圖像捕獲,采用的是 Apple 推薦的新的 AVFoundation 框架,攝像頭圖像格式設(shè)置為 kCVPixelFormatType_32ARGB(設(shè)置成其他的測試了無法得到圖像,系統(tǒng)不支持),通過 delegate 方式得到 CMSampleBufferRef 類型的 sample buffer 后,需要通過 CMSampleBufferGetImageBuffer 函數(shù)將其轉(zhuǎn)換為 CVImageBufferRef 類型的圖像緩沖(因?yàn)檫@里捕獲的是圖像數(shù)據(jù),并不是采樣數(shù)據(jù),所以不能用 CMSampleBufferGetDataBuffer)。然后通過 CVPixelBufferGetBaseAddress 函數(shù)得到圖像緩沖的首地址,用 CVPixelBufferGetDataSize 函數(shù)得到圖像緩沖區(qū)數(shù)據(jù)大小,但這里千萬要注意,不要以為這時(shí)獲取的圖像緩沖區(qū)數(shù)據(jù)就可以通過Java 的 Raster 和 DataBuffer 等方式來直接填充 Java 中的 BufferedImage(這里假定 BufferedImage 采用 TYPE_INT_ARGB,因?yàn)橄胫鴮?yīng) kCVPixelFormatType_32ARGB)。因?yàn)檫@么做了會發(fā)現(xiàn)圖像顏色完全是錯(cuò)亂的,事實(shí)上,我們通過計(jì)算就可以知道,對應(yīng)高、寬下的 TYPE_INT_ARGB 格式的 BufferedImage 圖像數(shù)據(jù)大小為 width × height × 4 字節(jié)(因?yàn)檫@時(shí)一個(gè)像素為 int 類型,4 字節(jié)大小),但 CVPixelBufferGetDataSize 得到的圖像數(shù)據(jù)大小總比前者要多 4 個(gè)字節(jié),可能是保存了其他的一些信息。所以這里直接創(chuàng)建 TYPE_INT_ARGB 格式的 BufferedImage 然后用 Raster 以及 DataBuffer 等方式進(jìn)行填充是行不通的。
這里就需要采取另外的方式,用 DataBufferByte、ComponentSampleModel、WritableRaster、ColorSpace、ColorModel 來構(gòu)建 BufferedImage,其實(shí)也是使用了 BufferedImage 的另一種不常用的、但效率非常高的構(gòu)造函數(shù)模式。當(dāng)然,前提是還需要通過 CVPixelBufferGetBytesPerRow 函數(shù)得到圖像中每個(gè)掃描行的字節(jié)數(shù),通過 CVPixelBufferGetHeight 函數(shù)得到圖像的高度,通過 CVPixelBufferGetWidth 函數(shù)得到圖像的寬度,后面會用得上。具體如以下代碼:
1 DataBufferByte dataBufferByte = new DataBufferByte(new byte[][] {dataBytes}, dataSize); // 這里假定 dataBytes 保存了本地獲取的圖像數(shù)據(jù),dataSize 為圖像數(shù)據(jù)大小(總是比“w * h * 每像素字節(jié)數(shù)”計(jì)算出來要大一點(diǎn))2 ComponentSampleModel componentSampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, width, height, 4, bytesPerRow, new int[] {1, 2, 3, 0}); // 自定義 BufferedImage 中的圖像格式,還是以字節(jié)來存儲每個(gè)像素,具體構(gòu)造函數(shù)的說明見 javadoc api3 WritableRaster writableRaster = Raster.createWritableRaster(componentSampleModel, dataBufferByte, new Point(0, 0)); // 創(chuàng)建包含具體圖像數(shù)據(jù)的柵格陣列4 ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); // 創(chuàng)建 RGB 顏色空間5 int[] nBits = {8, 8, 8, 8}; // 對應(yīng)源圖像數(shù)據(jù)的 ARGB,因?yàn)樵磮D像數(shù)據(jù)采用 32ARGB,等效于 4 個(gè)字節(jié),每個(gè)字節(jié)按順序分別表示 alpha、red、green、blue6 ColorModel colorModel = new ComponentColorModel(colorSpace, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); // 創(chuàng)建顏色模式7 BufferedImage bufferedImage = new BufferedImage(colorModel, writableRaster, false, null); // 構(gòu)建自定義像素格式的 BufferedImage
這種方式就不會出現(xiàn)問題了,本地庫捕獲的實(shí)時(shí)攝像頭圖像能夠正確填充到 Java 的 BufferedImage 中,而且效率非常高。
新聞熱點(diǎn)
疑難解答
圖片精選