swift - 查找两个 UIImage 之间的相似度百分比
问题描述
我正在尝试构建一个应用程序以查找两者之间的百分比相似性UIImage
,该应用程序捕获用户笔划铅笔并将其转换为UIImage
然后逐个像素地找到与存储在应用程序上的图像的相似性。
我找到了发现差异的代码(不是相似性),如果我有两个图像为 JPEG,那是有效的,但在我的情况下,当我单击按钮时会出现错误
convertoand:]: 无法识别的选择器发送到实例 0x10900f2d0"
该按钮将捕获笔画铅笔,然后在其中进行比较,然后将func compareImages(image1: imageStroke, image2: imageOnApp)
其打印在标签上:
@IBAction func btnFindSimilarity(_ sender: Any) {
let imgRect = CGRect(x: 0, y: 0, width: 400, height: 100)
let imageStroke = canvasView.drawing.image(from: imgRect, scale: 1.0)
let compareResult = compareImages(image1: imageStroke, image2: imageOnApp)
lblPrintScore.text = "\(String(describing: compareResult))"
}
查找差异代码:
func pixelValues(fromCGImage imageRef: CGImage?) -> [UInt8]?
{
var width = 0
var height = 0
var pixelValues: [UInt8]?
if let imageRef = imageRef {
width = imageRef.width
height = imageRef.height
let bitsPerComponent = imageRef.bitsPerComponent
let bytesPerRow = imageRef.bytesPerRow
let totalBytes = height * bytesPerRow
let bitmapInfo = imageRef.bitmapInfo
let colorSpace = CGColorSpaceCreateDeviceRGB()
var intensities = [UInt8](repeating: 0, count: totalBytes)
let contextRef = CGContext(data: &intensities,
width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo.rawValue)
contextRef?.draw(imageRef, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(width), height: CGFloat(height)))
pixelValues = intensities
}
return pixelValues
}
func compareImages(image1: UIImage, image2: UIImage) -> Double? {
guard let data1 = pixelValues(fromCGImage: image1.cgImage),
let data2 = pixelValues(fromCGImage: image2.cgImage),
data1.count == data2.count else {
return nil
}
let width = Double(image1.size.width)
let height = Double(image1.size.height)
return zip(data1, data2)
.enumerated()
.reduce(0.0) {
$1.offset % 4 == 3 ? $0 : $0 + abs(Double($1.element.0) - Double($1.element.1))
}
* 100 / (width * height * 3.0) / 255.0
}
我试图更改代码以找到相似之处,但我失败了
解决方案
虽然下面的示例是针对 SwiftUI 的,但代码可以在其他地方使用。我在 SO 上找到了如何从 UIImage 中提取颜色(请参阅findColors
如何使用 Swift 获取 UIImage 中像素的颜色?),并使用它比较 2 个图像的 rgb 相似性(请参阅 参考资料isRGBSimilar
)。
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var simili = 0.0
var body: some View {
Text(String(simili))
.onAppear {
guard let img1 = UIImage(named: "cat1.png") else { return }
guard let img2 = UIImage(named: "cat2.png") else { return }
print("---> img1: \(img1.size) img2: \(img2.size)")
if let percent = compareImages(image1: img1, image2: img2) {
print("----> percent: \(percent) \n")
simili = percent
}
}
}
// from: https://stackoverflow.com/questions/25146557/how-do-i-get-the-color-of-a-pixel-in-a-uiimage-with-swift
func findColors(_ image: UIImage) -> [UIColor] {
let pixelsWide = Int(image.size.width)
let pixelsHigh = Int(image.size.height)
guard let pixelData = image.cgImage?.dataProvider?.data else { return [] }
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
var imageColors: [UIColor] = []
for x in 0..<pixelsWide {
for y in 0..<pixelsHigh {
let point = CGPoint(x: x, y: y)
let pixelInfo: Int = ((pixelsWide * Int(point.y)) + Int(point.x)) * 4
let color = UIColor(red: CGFloat(data[pixelInfo]) / 255.0,
green: CGFloat(data[pixelInfo + 1]) / 255.0,
blue: CGFloat(data[pixelInfo + 2]) / 255.0,
alpha: CGFloat(data[pixelInfo + 3]) / 255.0)
imageColors.append(color)
}
}
return imageColors
}
func compareImages(image1: UIImage, image2: UIImage) -> Double? {
let data1 = findColors(image1)
let data2 = findColors(image2)
guard data1.count == data2.count else { return nil }
var similarr = [Bool]()
for i in data1.indices {
similarr.append(isRGBSimilar(data1[i], data2[i], 0.1)) // <-- set epsilon
}
let simi = similarr.filter{$0}
let result = (Double(simi.count * 100) / (Double(image1.size.width) * Double(image1.size.height)))
return result
}
// compare 2 colors to be within d of each other
func isRGBSimilar(_ f: UIColor, _ t: UIColor, _ d: CGFloat) -> Bool {
var r1: CGFloat = 0; var g1: CGFloat = 0; var b1: CGFloat = 0; var a1: CGFloat = 0
f.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
var r2: CGFloat = 0; var g2: CGFloat = 0; var b2: CGFloat = 0; var a2: CGFloat = 0
t.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
return abs(r1 - r2) <= d && abs(g1 - g2) <= d && abs(b1 - b2) <= d && abs(a1 - a2) <= d
}
推荐阅读
- typescript - 如何定义某个类的类型保护方法?
- django - DRF-如何使用APIView保存外键数据
- python - 使用 POST 请求发送数据在使用 AJAX 的 Django 中不起作用
- python - 如何通过Python在Excel中的多个单元格引用中组合值?
- youtube-iframe-api - YouTube iframe 不会从某些域加载视频
- javascript - 子菜单不适用于 ReactJs 中的多个项目?
- git - Eclipse git push to repository - origin 遇到了问题
- android - Android adb 连接 ipv6 问题
- kubernetes - 使用相同的主机但不同的目标端口将传入请求路由到部署(例如 wordpress 和 phpmyadmin)
- struct - 如何向编译器保证我使用的类型是具有字段 X 和 Y 的结构?