【发布时间】:2018-05-14 15:40:46
【问题描述】:
我正在尝试使用模型对灰度图像进行分类。
我使用一些 TensorflowJS 示例中提供的Webcam class 来使用网络摄像头获取图像。它运行良好,但返回的是 RGB 图像。
将此图像转换为灰度的最佳方法是什么?我是否需要计算这个从张量中获取每个元素并计算一个表示灰度图像的新矩阵,还是有更简单或更方便的方法?
【问题讨论】:
标签: tensorflow.js
我正在尝试使用模型对灰度图像进行分类。
我使用一些 TensorflowJS 示例中提供的Webcam class 来使用网络摄像头获取图像。它运行良好,但返回的是 RGB 图像。
将此图像转换为灰度的最佳方法是什么?我是否需要计算这个从张量中获取每个元素并计算一个表示灰度图像的新矩阵,还是有更简单或更方便的方法?
【问题讨论】:
标签: tensorflow.js
如果需要[width, height, 1]的形式:
tf.browser.fromPixels(image)
.mean(2)
.toFloat()
.expandDims(-1)
如果需要[1, width, height, 1]的形式:
tf.browser.fromPixels(image)
.mean(2)
.toFloat()
.expandDims(0)
.expandDims(-1)
【讨论】:
从 RGB 图像获取灰度的首选方法是根据 ITU-R 建议 (BT.601)。 matlab、OpenCV 和 Tensorflow (python) 都是这样做的。
灰色 = 0.2989 * R + 0.5870 * G + 0.1140 * B
如果是这样,我能想到的最好方法是使用一个简单的示例,该示例应该扩展到任何大小的图像:
// make an image that is size 2x2, with 3 color channels
x = tf.randomUniform([2, 2, 3]);
// print out the tensor so you know what you started with
x.print();
// the scalars needed for conversion of each channel
// per the formula: gray = 0.2989 * R + 0.5870 * G + 0.1140 * B
rFactor = tf.scalar(0.2989);
gFactor = tf.scalar(0.5870);
bFactor = tf.scalar(0.1140);
// separate out each channel. x.shape[0] and x.shape[1] will give you
// the correct dimensions regardless of image size
r = x.slice([0,0,0], [x.shape[0], x.shape[1], 1]);
g = x.slice([0,0,1], [x.shape[0], x.shape[1], 1]);
b = x.slice([0,0,2], [x.shape[0], x.shape[1], 1]);
// add all the tensors together, as they should all be the same dimensions.
gray = r.mul(rFactor).add(g.mul(gFactor)).add(b.mul(bFactor));
// check your work
gray.print();
【讨论】:
要将图像转换为灰度,应删除深度通道维度或将其减少为一。
给定一个张量 t,这里有两种方法
tensor.mean(2)
tensor.slice([0, 0, 0], [a.shape[0], a.shape[1], 1])
tf.toPixels 显示来自张量的图像。
3 - 转置频道 (HWC -> CHW)
通过转置通道,2D 底层图像可以在数组中展开,因为 toPixel 也可以与 2D 张量一起使用
const c = a.transpose([2, 0, 1]).unstack()[0];
const im = new Image()
im.crossOrigin = "anonymous";
im.src = "https://i.imgur.com/lVlPvCB.gif"
document.body.appendChild(im)
im.onload = () => {
const a = tf.fromPixels(im, 4)
const canvas = document.createElement('canvas');
const canvas1 = document.createElement('canvas');
const canvas2 = document.createElement('canvas');
tf.toPixels(a.mean(2).div(255.0), canvas);
const b = a.slice([0, 0, 0], [a.shape[0], a.shape[1], 1])
tf.toPixels(b, canvas1);
const c = a.transpose([2, 0, 1]).unstack()[0];
tf.toPixels(c, canvas2);
document.body.append(canvas);
document.body.append(canvas1);
document.body.append(canvas2);
}
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.1/dist/tf.min.js"> </script>
</head>
<body>
</body>
</html>
wikipedia中描述的灰度转换
const rgb = tf.tensor1d([0.2989, 0.587, 0.114])
return tf.sum(image.mul(rgb), 2) // broadcasting
【讨论】:
表示图像的 3D 张量具有以下形状:(height, width, number_of_channels)。因此,RGB 图像具有形状(高度、宽度、3)。
为了转换为灰度,我使用最后一个轴(number_of_channels 轴)计算了平均值。因此,每个像素值现在都是 R、G 和 B 值的平均值。
grayscale_image = image.mean(2)
此操作会移除张量的最后一个维度,返回一个形状为(高度、宽度)的张量。
为了保持形状(高度、宽度、通道数),我扩大了尺寸:
final_image = grayscale_image.expandDims(2)
final_image 具有形状(高度、宽度、1)
【讨论】:
唯一对我有用的解决方案是首先平均 RGB 通道值,然后使用 tf.stack 复制平均值并再次使图像形状适合 RGB。
一个例子:
// load a tensor from a canvas image
let t = await tf.fromPixels(canvas, 3);
// at this point shape should be [width, height, 3]
// now average along the channel dimension to get average of RGB for each pixel
t = t.mean(2);
// at this point shape should be [width, height]
// finally, repeat each monochrome value three times to get our shape back to RGB
t = tf.stack([t, t, t], 2);
// final shape is again [width, height, 3]
【讨论】: