12

这是我需要实现的:

  • 从相机或图库中获取图像
  • 从图像中删除背景并保存
  • 背景应该是黑色或白色
  • 还需要连同背景一起去除阴影

结果示例:

原始图像

在此处输入图像描述

结果图像

在此处输入图像描述

这是我尝试过的:

CGFloat colorMasking[6]={222,255,222,255,222,255};
CGImageRef imageRef = CGImageCreateWithMaskingColors([IMG CGImage], colorMasking);
UIImage  *resultThumbImage = [UIImage imageWithCGImage:imageRef scale:ThumbImage.scale orientation:IMG.imageOrientation];

它仅适用于白色背景。它不是更有效。我需要达到我在上面的图像中放置的确切结果。

我还参考了一些参考资料:

iOS如何屏蔽图像背景颜色

如何在iphone应用程序中删除图像的背景?

将拍摄图像的背景颜色从相机更改为白色

有人可以帮助我实现这一目标吗?

任何参考或将不胜感激。

提前致谢。

4

2 回答 2

8

一般来说,根据经验,背景颜色与所有其他颜色的差异越大,就越容易将图像分割成前景和背景。在这种情况下,正如@Chris 已经建议的那样,可以使用简单的色度键实现。下面是我在Wikipedia上描述的键控的快速实现(它是用 C++ 编写的,但将其转换为 Objective-C 应该很容易):

/**
 * @brief Separate foreground from background using simple chroma keying.
 *
 * @param imageBGR   Image with monochrome background
 * @param chromaBGR  Color of the background (using channel order BGR and range [0, 255])
 * @param tInner     Inner threshold, color distances below this value will be counted as foreground
 * @param tOuter     Outer threshold, color distances above this value will be counted as background
 *
 * @return  Mask (0 - background, 255 - foreground, [1, 255] - partially fore- and background)
 *
 * Details can be found on [Wikipedia][1].
 *
 * [1]: https://en.wikipedia.org/wiki/Chroma_key#Programming
 */
cv::Mat1b chromaKey( const cv::Mat3b & imageBGR, cv::Scalar chromaBGR, double tInner, double tOuter )
{
    // Basic outline:
    //
    // 1. Convert the image to YCrCb.
    // 2. Measure Euclidean distances of color in YCrBr to chroma value.
    // 3. Categorize pixels:
    //   * color distances below inner threshold count as foreground; mask value = 0
    //   * color distances above outer threshold count as background; mask value = 255
    //   * color distances between inner and outer threshold a linearly interpolated; mask value = [0, 255]

    assert( tInner <= tOuter );

    // Convert to YCrCb.
    assert( ! imageBGR.empty() );
    cv::Size imageSize = imageBGR.size();
    cv::Mat3b imageYCrCb;
    cv::cvtColor( imageBGR, imageYCrCb, cv::COLOR_BGR2YCrCb );
    cv::Scalar chromaYCrCb = bgr2ycrcb( chromaBGR ); // Convert a single BGR value to YCrCb.

    // Build the mask.
    cv::Mat1b mask = cv::Mat1b::zeros( imageSize );
    const cv::Vec3d key( chromaYCrCb[ 0 ], chromaYCrCb[ 1 ], chromaYCrCb[ 2 ] );

    for ( int y = 0; y < imageSize.height; ++y )
    {
        for ( int x = 0; x < imageSize.width; ++x )
        {
            const cv::Vec3d color( imageYCrCb( y, x )[ 0 ], imageYCrCb( y, x )[ 1 ], imageYCrCb( y, x )[ 2 ] );
            double distance = cv::norm( key - color );

            if ( distance < tInner )
            {
                // Current pixel is fully part of the background.
                mask( y, x ) = 0;
            }
            else if ( distance > tOuter )
            {
                // Current pixel is fully part of the foreground.
                mask( y, x ) = 255;
            }
            else
            {
                // Current pixel is partially part both, fore- and background; interpolate linearly.
                // Compute the interpolation factor and clip its value to the range [0, 255].
                double d1 = distance - tInner;
                double d2 = tOuter   - tInner;
                uint8_t alpha = static_cast< uint8_t >( 255. * ( d1 / d2 ) );

                mask( y, x ) = alpha;
            }
        }
    }

    return mask;
}

可以在这个Github Gist中找到一个完整的工作代码示例。

不幸的是,您的示例不符合该经验法则。由于前景和背景仅在强度上有所不同,因此很难(甚至不可能)找到一个单一的全局参数集来实现良好的分离:

  1. 物体周围有黑线,但物体内部没有孔 ( tInner=50, tOuter=90) 面具 1 背景替换 1

  2. 对象周围没有黑线,但对象内部有孔 ( tInner=100, tOuter=170) 面具 2 背景替换 2

因此,如果您无法更改图像的背景,则需要更复杂的方法。但是,一个快速简单的示例实现有点超出范围,但您可能希望查看图像分割alpha matting的相关领域。

于 2015-07-19T13:37:33.120 回答
4

我认为您需要使用色键 - 尽管您选择黑色,但我认为它应该可以工作。

一个快速的谷歌想出了这个

于 2015-07-14T08:00:27.320 回答