获取子图像的像素
Get Pixels of SubImage
考虑以下简单的 Clojure 与 Java 互操作以读取图像的像素数据:
(import java.awt.image.BufferedImage)
(import javax.imageio.ImageIO)
(require '[clojure.java.io :as io])
(defn read-image [path]
(ImageIO/read (io/file path)))
(defn get-sub-image [img x y w h]
(.getSubimage img x y w h))
;; this assumes that images have no fourth alpha channel:
(defn get-pixels
([img x y w h] (get-pixels (get-sub-image img x y w h)))
([img] (let [bytes (-> img .getRaster .getDataBuffer .getData)]
(map vec (partition 3 bytes)))))
这对于获取整个图像的像素效果很好,如下所示:
(def all-pixels (get-pixels (read-image "path/to/img.jpg")))
(nth all-pixels 0)
;; [34 56 7]
(count all-pixels)
;; 122343
但是,当尝试使用附加坐标参数调用 get-pixels 时,结果仍然包含整个数据:
(def some-pixels (get-pixels (read-image "path/to/img.jpg") 0 0 2 2))
(count some-pixels)
;; 122343
这里我希望只接收到 4 个像素。漏洞在哪里?
也欢迎对将图像数据处理为惰性序列的一般方法提出任何意见。
阅读代码有点困难,但如果我们浏览源代码:
public BufferedImage More ...getSubimage (int x, int y, int w, int h) {
return new BufferedImage (colorModel,
raster.createWritableChild(x, y, w, h,
0, 0, null),
colorModel.isAlphaPremultiplied(),
properties);
}
这个方法调用raster.createWritableChild
让我们看看...
public WritableRaster More ...createWritableChild(int parentX, int parentY,
int w, int h,
int childMinX, int childMinY,
int bandList[]) {
if (parentX < this.minX) {
throw new RasterFormatException("parentX lies outside raster");
}
if (parentY < this.minY) {
throw new RasterFormatException("parentY lies outside raster");
}
if ((parentX+w < parentX) || (parentX+w > this.width + this.minX)) {
throw new RasterFormatException("(parentX + width) is outside raster");
}
if ((parentY+h < parentY) || (parentY+h > this.height + this.minY)) {
throw new RasterFormatException("(parentY + height) is outside raster");
}
SampleModel sm;
// Note: the SampleModel for the child Raster should have the same
// width and height as that for the parent, since it represents
// the physical layout of the pixel data. The child Raster's width
// and height represent a "virtual" view of the pixel data, so
// they may be different than those of the SampleModel.
if (bandList != null) {
sm = sampleModel.createSubsetSampleModel(bandList);
}
else {
sm = sampleModel;
}
int deltaX = childMinX - parentX;
int deltaY = childMinY - parentY;
return new WritableRaster(sm,
getDataBuffer(),
new Rectangle(childMinX,childMinY,
w, h),
new Point(sampleModelTranslateX+deltaX,
sampleModelTranslateY+deltaY),
this);
}
如您所见,创建的新栅格与其父栅格具有相同的 DataBuffer(可能允许在更新子图像时修改整个图像)所以当您执行
img .getRaster .getDataBuffer
你得到了整个图像的数据缓冲区。
我当然没有测试,但 BufferedImage.getData(Rectangle rect)
应该 return 一个有自己的 DataBuffer 的新栅格,然后像你那样做
编辑:Anton Harald (op) 的最终测试答案
(defn get-data [img x y w h]
(-> (.getData img (Rectangle. x y w h))
.getDataBuffer
.getData))
考虑以下简单的 Clojure 与 Java 互操作以读取图像的像素数据:
(import java.awt.image.BufferedImage)
(import javax.imageio.ImageIO)
(require '[clojure.java.io :as io])
(defn read-image [path]
(ImageIO/read (io/file path)))
(defn get-sub-image [img x y w h]
(.getSubimage img x y w h))
;; this assumes that images have no fourth alpha channel:
(defn get-pixels
([img x y w h] (get-pixels (get-sub-image img x y w h)))
([img] (let [bytes (-> img .getRaster .getDataBuffer .getData)]
(map vec (partition 3 bytes)))))
这对于获取整个图像的像素效果很好,如下所示:
(def all-pixels (get-pixels (read-image "path/to/img.jpg")))
(nth all-pixels 0)
;; [34 56 7]
(count all-pixels)
;; 122343
但是,当尝试使用附加坐标参数调用 get-pixels 时,结果仍然包含整个数据:
(def some-pixels (get-pixels (read-image "path/to/img.jpg") 0 0 2 2))
(count some-pixels)
;; 122343
这里我希望只接收到 4 个像素。漏洞在哪里?
也欢迎对将图像数据处理为惰性序列的一般方法提出任何意见。
阅读代码有点困难,但如果我们浏览源代码:
public BufferedImage More ...getSubimage (int x, int y, int w, int h) {
return new BufferedImage (colorModel,
raster.createWritableChild(x, y, w, h,
0, 0, null),
colorModel.isAlphaPremultiplied(),
properties);
}
这个方法调用raster.createWritableChild
让我们看看...
public WritableRaster More ...createWritableChild(int parentX, int parentY,
int w, int h,
int childMinX, int childMinY,
int bandList[]) {
if (parentX < this.minX) {
throw new RasterFormatException("parentX lies outside raster");
}
if (parentY < this.minY) {
throw new RasterFormatException("parentY lies outside raster");
}
if ((parentX+w < parentX) || (parentX+w > this.width + this.minX)) {
throw new RasterFormatException("(parentX + width) is outside raster");
}
if ((parentY+h < parentY) || (parentY+h > this.height + this.minY)) {
throw new RasterFormatException("(parentY + height) is outside raster");
}
SampleModel sm;
// Note: the SampleModel for the child Raster should have the same
// width and height as that for the parent, since it represents
// the physical layout of the pixel data. The child Raster's width
// and height represent a "virtual" view of the pixel data, so
// they may be different than those of the SampleModel.
if (bandList != null) {
sm = sampleModel.createSubsetSampleModel(bandList);
}
else {
sm = sampleModel;
}
int deltaX = childMinX - parentX;
int deltaY = childMinY - parentY;
return new WritableRaster(sm,
getDataBuffer(),
new Rectangle(childMinX,childMinY,
w, h),
new Point(sampleModelTranslateX+deltaX,
sampleModelTranslateY+deltaY),
this);
}
如您所见,创建的新栅格与其父栅格具有相同的 DataBuffer(可能允许在更新子图像时修改整个图像)所以当您执行
img .getRaster .getDataBuffer
你得到了整个图像的数据缓冲区。
我当然没有测试,但 BufferedImage.getData(Rectangle rect)
应该 return 一个有自己的 DataBuffer 的新栅格,然后像你那样做
编辑:Anton Harald (op) 的最终测试答案
(defn get-data [img x y w h]
(-> (.getData img (Rectangle. x y w h))
.getDataBuffer
.getData))