我试图建立一个应用程序,以找到相似性作为两个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 ?),并使用它,我比较了两张图像的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
}