我正在尝试使用 OpenCV 3.1 Java 操作单个图像像素。 OpenCV 将图像读取为字节数组。我想将彩色图像转换为灰度图像,而不用函数 Y = R*0.299 + G*0.587 + B*0.114 更改图像 channel 总数,因此结果仍然是 RGB 图像(3 个颜色 channel )。根据我的理解,因为0.299 + 0.587 + 0.114 = 1之和,虽然字节范围是-128到127,但在转换为字节时应该不会出现下溢或溢出问题。但结果很奇怪,因为图像的一些白色区域变成了黑色,而一些黑色区域变成了白色。我假设发生了下溢或溢出。这是我的代码:
List<Mat> channels = new ArrayList<>();
Core.split(intImage, channels);
int totalInt = (int)(channels.get(0).total());
byte[] blue = new byte[totalInt];
byte[] green = new byte[totalInt];
byte[] red = new byte[totalInt];
channels.get(0).get(0, 0, blue);
channels.get(1).get(0, 0, green);
channels.get(2).get(0, 0, red);
for (int i = 0; i < totalInt; i++) {
byte s = (byte)(blue[i]*0.114 + green[i]*0.587 + red[i]*0.299);
blue[i] = red[i] = green[i] = s;
}
channels.get(0).put(0, 0, blue);
channels.get(1).put(0, 0, green);
channels.get(2).put(0, 0, red);
Mat gray = new Mat();
Core.merge(channels, gray);
我尝试将图像转换为CvType.CV_16S
,它代表无符号短整型,到目前为止,它的工作没有问题。转换为 CV_8UC3 仍以字节为单位。我担心堆问题,因为当我尝试使用 int(即 CV_32S)时,一些大图像会发生堆错误。所以这是我的问题:
- 如果可以,我该如何防止或处理这些溢出/下溢。我仍然考虑使用字节,因为它会减少堆/内存的使用。
- 如果第一个是唯一的选择,我如何才能将 CV_16S 直接转换为 BufferedImage 而无需转换回原始 Mat 类型,因为我使用 Swing 来显示图像。
我找到了从Mat转换为BufferedImage
的方法,如下:
public BufferedImage toBufferedImage(Mat matrix) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (matrix.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
BufferedImage image = new BufferedImage(matrix.cols(),
matrix.rows(), type);
final byte[] targetPixels =
((DataBufferByte)image.getRaster().getDataBuffer()).getData();
matrix.get(0, 0, targetPixels);
return image;
}
请解释一下字节转换发生了什么。
最佳答案
计算
blue[i]*0.114 + green[i]*0.587 + red[i]*0.299
(您绝对应该更改为
red[i]*0.299 + green[i]*0.587 + blue[i]*0.114
保持 RGB 顺序不变!)
发生在字节值上。正如您已经指出的,这些值是负。这会导致计算出错误的结果。
尽管对于(有符号)字节
,负值-16和正值240用相同的位模式表示,但它们仍然是不同的值,必须考虑到这一点在计算过程中。
考虑以下示例:
public class PixelBytes
{
public static void main(String[] args)
{
byte r = (byte)(100);
byte g = (byte)(240);
byte b = (byte)0;
compute(r, g, b);
}
private static byte compute(byte r, byte g, byte b)
{
byte sr = (byte) (r * 0.299);
byte sg = (byte) (g * 0.587);
byte sb = (byte) (b * 0.114);
byte s = (byte) (sr + sg + sb);
byte tr = (byte) (toUnsignedInt(r) * 0.299);
byte tg = (byte) (toUnsignedInt(g) * 0.587);
byte tb = (byte) (toUnsignedInt(b) * 0.114);
byte t = (byte) (tr + tg + tb);
System.out.println("For " + r + " " + g + " " + b);
System.out.println("add " + sr + " " + sg + " " + sb + " to get " + s);
System.out.println("or " + tr + " " + tg + " " + tb + " tp get " + t);
return s;
}
// Same as Byte#toUnsignedInt in Java 8
private static int toUnsignedInt(byte b)
{
return ((int) b) & 0xff;
}
}
输出将是
For 100 -16 0
add 29 -9 0 to get 20
or 29 -116 0 tp get -87
清楚地表明结果有所不同,具体取决于是否使用负值,或者是否首先将值转换为其“真实”无符号值。
所以灰度像素值的计算可以用这样的方法来完成:
private static byte computeLuminance(byte r, byte g, byte b)
{
int ir = r & 0xFF;
int ig = g & 0xFF;
int ib = b & 0xFF;
return (byte)(ir * 0.299 + ig * 0.587 + ib * 0.114);
}
关于java - 在 Opencv 3.1 Java 中操作单个像素时如何防止或处理字节溢出和字节下溢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41117833/