【问题标题】:Detect black pixel in image iOS检测图像iOS中的黑色像素
【发布时间】:2026-01-08 05:50:01
【问题描述】:

到目前为止,我正在逐个搜索每个像素,检查颜色并查看它是否为黑色......如果不是,我将继续下一个像素。这需要很长时间,因为我只能检查大约。每秒 100 像素(加速我的 NSTimer 会冻结应用程序,因为它检查速度不够快。)所以无论如何我可以让 Xcode 返回所有黑色像素并忽略其他所有内容,所以我只需要检查这些像素而不是每个像素。我正在尝试检测图像上最左侧的黑色像素。

这是我当前的代码。

- (void)viewDidLoad {
    timer = [NSTimer scheduledTimerWithTimeInterval: 0.01
                                             target: self
                                           selector:@selector(onTick:)
                                           userInfo: nil repeats:YES];
    y1 = 0;
    x1 = 0;
    initialImage = 0;
    height1 = 0;
    width1 = 0;
}

-(void)onTick:(NSTimer *)timer {
    if (initialImage != 1) {
        /*
        IMAGE INITIALLY GETS SET HERE... "image2.image = [blah blah blah];" took this out for non disclosure reasons
        */
        initialImage = 1;
    }
    //image2 is the image I'm checking the pixels of.
    width1 = (int)image2.size.width;
    height1 = (int)image2.size.height;
    CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(image2.CGImage));
    const UInt32 *pixels = (const UInt32*)CFDataGetBytePtr(imageData);
    if ( (pixels[(x1+(y1*width1))]) == 0x000000) { //0x000000 is black right?
        NSLog(@"black!");
        NSLog(@"x = %i", x1);
        NSLog(@"y = %i", y1);
    }else {
        NSLog(@"val: %lu", (pixels[(x1+(y1*width1))]));
        NSLog(@"x = %i", x1);
        NSLog(@"y = %i", y1);
        x1 ++;
        if (x1 >= width1) {
            y1 ++;
            x1 = 0;
        }
    }
    if (y1 > height1) {
        /*
        MY UPDATE IMAGE CODE GOES HERE (IMAGE CHANGES EVERY TIME ALL PIXELS HAVE BEEN CHECKED
        */
        y1 = 0;
        x1 = 0;
    }

另外,如果一个像素非常接近黑色但不是完全黑色怎么办...我可以在某处添加一个误差范围,以便它仍然可以检测到 95% 黑色的像素吗?谢谢!

【问题讨论】:

    标签: objective-c ios image colors pixel


    【解决方案1】:

    你为什么要使用计时器?为什么不在你的函数中有一个双 for 循环来循环图像中所有可能的 x 和 y 坐标?当然,这比每秒最多检查 100 个像素要快得多。您可能希望在外循环中有 x(宽度)坐标,在内循环中有 y(高度)坐标,这样您就可以一次有效地从左到右扫描一列像素,因为您正在尝试查找最左边的黑色像素。

    另外,您确定图像中的每个像素都有一个 4 字节 (Uint32) 表示吗?标准位图每个像素有 3 个字节。要检查像素是否接近黑色,您只需分别检查像素中的每个字节并确保它们都小于某个阈值。

    编辑:好的,因为您使用的是 UIGetScreenImage,我将假设它是每像素 4 字节。

    const UInt8 *pixels = CFDataGetBytePtr(imageData);
    UInt8 blackThreshold = 10; // or some value close to 0
    int bytesPerPixel = 4;
    for(int x = 0; x < width1; x++) {
      for(int y = 0; y < height1; y++) {
        int pixelStartIndex = (x + (y * width1)) * bytesPerPixel;
        UInt8 alphaVal = pixels[pixelStartIndex]; // can probably ignore this value
        UInt8 redVal = pixels[pixelStartIndex + 1];
        UInt8 greenVal = pixels[pixelStartIndex + 2];
        UInt8 blueVal = pixels[pixelStartIndex + 3];
        if(redVal < blackThreshold && blueVal < blackThreshold && greenVal < blackThreshold) {
          //This pixel is close to black...do something with it
        }
      }
    }
    

    如果 bytesPerPixel 为 3,则相应地更改该值,从 for 循环中删除 alphaVal,并从红色、绿色和蓝色值的索引中减去 1。

    另外,我目前的理解是 UIGetScreenImage 被认为是 Apple 可能会或可能不会拒绝您使用的私有函数。

    【讨论】:

    • 如何将其更改为 3bit?该图像只是我使用CGImageRef UIGetScreenImage(); 捕获的屏幕上一部分的屏幕截图另外我完全不使用计时器,我不熟悉while-infinite 循环,我知道如何设置它们我只是很少使用它们,所以我想我忘了哈哈。另外,有没有一种方法可以检测数组中的所有像素,只返回黑色像素的 x 和 y 坐标,而不是单独检查每个像素(即使我正在使用循环?)
    • 循环没有什么无限的,它只是超过了图像的宽度和高度。您将不得不以一种或另一种方式检查所有像素。没有其他方法可以确定哪个是黑色的。请稍等,我会用一些代码更新我的帖子。
    • 很棒的代码!谢谢!对不起,我在想像 while i
    • 我的图像是 150x170 像素,所以这个方法需要一段时间!我不太关心图像的质量,那么对于这种像素检查方法调整图像大小的正确方法是什么?现在它只是一个 UIImage。我在想一些类似CGImageCreateWithImageInRect 的东西,但我认为这不会扩大它,只是裁剪它..
    • 您的方法效果很好我只需要一种调整图像大小的好方法,这样我就不必检查数万像素...我想将其缩放到 15x17 而不是 150x170,黑色区域足够大,缩放后我仍然会有黑色像素。 (顺便说一句,我确实必须将其更改为 bytesPerPixel3 并忽略 alpha!
    【解决方案2】:

    我不是像素级图像处理方面的专家,但我的第一个想法是:为什么要使用计时器来执行此操作?这会产生大量开销,并使代码不那么清晰易读。 (我认为它也使它成为线程不安全的。)开销不仅来自计时器本身,还因为您每次都在进行所有数据设置。

    使用循环来迭代像素怎么样?

    此外,您正在泄漏 imageData(因为您使用“复制”方法创建它并且从不释放它)。目前,每个计时器触发一次(如果您正在处理除最小图像之外的所有图像,您的 imageData 可能非常大),因此您可能会泄漏 记忆。

    【讨论】:

    • 我怎样才能阻止我的内存泄漏?我过去从来没有处理过这个问题,但我可以看到这样的事情会成为一个问题?你能给我上一堂小课吗? :)
    • 你真的真的需要阅读核心基础内存管理指南 (developer.apple.com/library/mac/#documentation/CoreFoundation/…),至少前三部分。在这种情况下,简短的是在方法结束时调用 CGDataProviderRelease(imageData)。
    • 糟糕,我的意思是 CFRelease(imageData)... 电线在大脑中交叉。
    【解决方案3】:

    你不应该用计时器来做这件事(或者无论如何我都想不出任何理由!)。

    您的图片有多大?在一个循环中合理快速地处理整个图像应该是可行的。

    【讨论】: