我尝试使用BezierPath根据每个像素的透明度绘制图像的轮廓。
不过,我觉得逻辑上有点问题我的逻辑也画出了内在的轮廓。
我只想用BezierPath绘制外部轮廓。我得到了什么(第一个形状是原始图像,第二个是bezierPath
):
我的代码:
func processImage(_ image: UIImage) -> UIBezierPath? {
guard let cgImage = image.cgImage else {
print("Error: Couldn't get CGImage from UIImage")
return nil
}
let width = cgImage.width
let height = cgImage.height
// Create a context to perform image processing
let colorSpace = CGColorSpaceCreateDeviceGray()
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)
guard let context = context else {
print("Error: Couldn't create CGContext")
return nil
}
// Draw the image into the context
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
// Perform Canny edge detection
guard let edgeImage = context.makeImage() else {
print("Error: Couldn't create edge image")
return nil
}
// Create a bezier path for the outline of the shape
let bezierPath = UIBezierPath()
// Iterate over the image pixels to find the edges
for y in 0..<height {
for x in 0..<width {
let pixel = edgeImage.pixel(x: x, y: y)
if pixel > 0 {
let leftPixel = (x > 0) ? edgeImage.pixel(x: x - 1, y: y) : 0
let rightPixel = (x < width - 1) ? edgeImage.pixel(x: x + 1, y: y) : 0
let abovePixel = (y > 0) ? edgeImage.pixel(x: x, y: y - 1) : 0
let belowPixel = (y < height - 1) ? edgeImage.pixel(x: x, y: y + 1) : 0
if leftPixel == 0 || rightPixel == 0 || abovePixel == 0 || belowPixel == 0 {
bezierPath.move(to: CGPoint(x: CGFloat(x), y: CGFloat(y)))
bezierPath.addLine(to: CGPoint(x: CGFloat(x) + 1.0, y: CGFloat(y) + 1.0))
}
}
}
}
return bezierPath
}
extension CGImage {
func pixel(x: Int, y: Int) -> UInt8 {
let data = self.dataProvider!.data
let pointer = CFDataGetBytePtr(data)
let bytesPerRow = self.bytesPerRow
let pixelInfo = (bytesPerRow * y) + x
return pointer![pixelInfo]
}
}
1条答案
按热度按时间axr492tv1#
从注解中,您找到了算法(摩尔Neighborhood Tracing)。下面是一个很好地解决您的问题的实现。我会评论一些你可能会考虑的改进。
首先,您需要将数据放入缓冲区,每个像素一个字节。你似乎知道如何做到这一点,所以我就不赘述了。0应该是“透明的”,非0应该是“填充的”。在文献中,这些通常是白色(0)背景上的黑色(1)行,因此我将使用这种命名。
我发现的最好的介绍(经常被引用)是乔治Ghuneim的Contour Tracing网站。非常有用的网站。我看到过一些MNT的实现会过度检查一些像素。我尝试遵循Abeer仔细描述的算法来避免这种情况。
我想对这段代码做更多的测试,但它可以处理您的情况。
首先,该算法在单元格网格上操作:
还有“方向”的概念。这有两种形式:从中心到8个相邻单元中的一个的方向,以及“回溯”方向,这是在搜索期间“进入”单元的方向
细胞可以在给定的方向上前进:
最后是摩尔Neighbor算法:
这将返回单元格列表。它可以转换为CGPath,如下所示:
生成以下路径:
这里有我使用的完整示例代码a gist。(此示例代码并不意味着是如何准备图像以进行处理的良好示例。我只是把它放在一起来研究算法。)
对未来工作的一些思考: