java - 在 Opencv 3.1 Java 中操作单个像素时如何防止或处理字节溢出和字节下溢

标签 java image opencv

我正在尝试使用 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/

相关文章:

java - 内部框架不工作

java - 图像未加载

c# - 将图像导入图像框中

C#人脸识别

matlab - 将级联分类器从 Matlab 导入到 OpenCV 3.0

java - 我不明白Android AutoCompleteTextView ArrayAdapter的构造函数

java - 根据日期时间对对象的数组列表进行排序

java - Socket监听多个数据包,没有无限循环

c++ - opencv多 channel 元素访问

android - Android 上矢量 <DMatch> OpenCV 的问题