123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- //
- // ImageProcess.swift
- // KdanAuto
- //
- // Created by 朱东勇 on 2023/2/7.
- //
- import Foundation
- import AppKit
- import CoreImage
- import CryptoKit
- class ImageProcess : NSObject {
-
- // Image compare
- class func compareJPEG(_ resultPath:String, checkPath:String, processCover:Bool) -> Double {
- autoreleasepool {
- if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
- return -1
- }
-
- let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
- let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
-
- if nil == rImage || nil == cImage {
- return -1
- }
-
- let resultImage = rImage!
- let checkImage = cImage!
-
- var resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
- let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
-
- var rWidth = resultImageRep.pixelsWide
- var rHeight = resultImageRep.pixelsHigh
- // let rBitPerPixel = resultImageRep.bitsPerPixel / 8
- // let rBytePerRow = resultImageRep.bytesPerRow
-
- let cWidth = checkImageRep.pixelsWide
- let cHeight = checkImageRep.pixelsHigh
- // let cBitPerPixel = checkImageRep.bitsPerPixel / 8
- // let cBytePerRow = checkImageRep.bytesPerRow
-
- // 图像为等比例、且长宽比相同
- if rHeight*cHeight > 0 &&
- fabs(Double(rWidth-cWidth)) > 1 &&
- fabs(Double(rWidth)/Double(rHeight) - Double(cWidth)/Double(cHeight)) < 0.001 {
- var ciImage = CIImage(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
- ciImage = ciImage.transformed(by: CGAffineTransformMakeScale(Double(cWidth)/Double(rWidth), Double(cHeight)/Double(rHeight)))
- let context = CIContext()
- let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
- if cgImage != nil {
- resultImageRep = NSBitmapImageRep(cgImage: cgImage!);
-
- rWidth = resultImageRep.pixelsWide
- rHeight = resultImageRep.pixelsHigh
- }
- }
-
- let maxWidth = min(rWidth, cWidth) - 1
- let maxHeight = min(rHeight, cHeight) - 1
-
- // check background color
- // 挑选图片 对角斜线 上的相素进行识别
- let markInfo = NSMutableDictionary.init()
- for w in 0...maxWidth {
- let x = Int(w)
- for mark in 0...1 {
- let color = checkImageRep.colorAt(x: min(x, cWidth-1), y: min(mark == 0 ? x : (maxWidth - x), (cHeight-1))) as! NSColor
-
- if (markInfo[color] != nil) {
- let count = (markInfo[color] as? NSNumber)!.intValue
-
- markInfo[color] = NSNumber.init(value: count + 1)
- }else {
- markInfo[color] = NSNumber.init(value: 1)
- }
- }
- }
-
- var maxCount = Int(0);
- var bgColor : NSColor = NSColor.clear
- for color in markInfo.allKeys {
- let count = (markInfo[color] as? NSNumber)!.intValue
-
- if count > maxCount {
- maxCount = count
- bgColor = color as! NSColor
- }
- }
-
- // if nil != bgColor {
- // NSLog(String("识别到背景色\(bgColor)"))
- // }
-
- // let bg_r = Int(bgColor.redComponent*255);
- // let bg_g = Int(bgColor.redComponent*255);
- // let bg_b = Int(bgColor.redComponent*255);
- // let bg_a = Int(bgColor.redComponent*255);
- //
- let data = NSMutableData.init(length: cWidth * cHeight * 4)
- // Compare
- let compareDifValue = DataModel.shared.comparativeDifference()
- var equalCount = 0 as Double
- var bgCount = 0 as Double
-
- let semaphore = DispatchSemaphore(value: 0)
- let operateQueue = OperationQueue()
- operateQueue.maxConcurrentOperationCount = 20;
- var processCount = Int(0)
-
- for w in 0...maxWidth {
- let x = Int(w)
-
- operateQueue.addOperation {
- autoreleasepool {
- for h in 0...maxHeight {
- let y = Int(h)
-
- objc_sync_enter(self)
- let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
- let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
- objc_sync_exit(self)
-
- let cr = Int(cColor.redComponent*255)
- let cg = Int(cColor.greenComponent*255)
- let cb = Int(cColor.blueComponent*255)
- let ca = Int(cColor.blueComponent*255)
-
- let rr = Int(rColor.redComponent*255)
- let rg = Int(rColor.greenComponent*255)
- let rb = Int(rColor.blueComponent*255)
- let ra = Int(rColor.blueComponent*255)
-
- // if (cColor.isEqual(to: rColor)) {
- if (abs(cr - rr) <= compareDifValue &&
- abs(cg - rg) <= compareDifValue &&
- abs(cb - rb) <= compareDifValue &&
- abs(ca - ra) <= compareDifValue) {
- equalCount = equalCount + 1
-
- if cColor.isEqual(to: bgColor) {
- objc_sync_enter(self)
- bgCount += 1
- objc_sync_exit(self)
- }
- }else if (processCover && nil != data){
- let addr = cWidth * 4 * y + x * 4
-
- objc_sync_enter(self)
- var r = uint8(255)
- data!.replaceBytes(in: NSRange.init(location: addr+0, length: 1), withBytes: &r)
- var g = uint8(255 * rColor.greenComponent/2)
- data!.replaceBytes(in: NSRange.init(location: addr+1, length: 1), withBytes: &g)
- var b = uint8(255 * rColor.blueComponent/2)
- data!.replaceBytes(in: NSRange.init(location: addr+2, length: 1), withBytes: &b)
- var a = uint8(255 * rColor.alphaComponent * 0.7)
- data!.replaceBytes(in: NSRange.init(location: addr+3, length: 1), withBytes: &a)
- objc_sync_exit(self)
- }
- }
-
- processCount = processCount+1
- if (processCount >= maxWidth) {
- semaphore.signal()
- }
- }
- }
- }
-
- semaphore.wait()
-
- let outDegree = Double(max(equalCount-bgCount, 1)/(max(Double(cWidth) * Double(cHeight)-bgCount, 1)) * 100.0)
- if (abs(outDegree - 100) > 0 && processCover && nil != data) {
- DispatchQueue.global().async {
- autoreleasepool {
- let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
- let dataProvider = CGDataProvider.init(data: cfData!)
-
- let colorSpace = CGColorSpaceCreateDeviceRGB()
- let cgImage = CGImage.init(width: cWidth,
- height: cHeight,
- bitsPerComponent: 8,
- bitsPerPixel: 8 * 4,
- bytesPerRow: cWidth * 4,
- space: colorSpace,
- bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
- provider: dataProvider!,
- decode: nil,
- shouldInterpolate: true,
- intent: CGColorRenderingIntent.defaultIntent)
-
- if nil != cgImage {
- let coverPath = NSString(format: "%@_cover.png", NSString(string: resultPath).deletingPathExtension) as! String
- let rep = NSBitmapImageRep.init(cgImage: cgImage!)
-
- if let saveData = rep.representation(using: .png, properties: [:]) {
- let url = URL.init(fileURLWithPath: coverPath, isDirectory: false)
- try? saveData.write(to: url)
- }
- }
- }
- }
- }
-
- NSLog(String("过滤点数目\(bgCount)"))
-
- return outDegree
- }
- }
-
- class func compareJPEG(_ resultPath:String, checkPath:String, processCover:Bool, complention:@escaping (_ degree:Double) -> ()) {
- autoreleasepool {
- if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
- complention(-1)
- return
- }
-
- let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
- let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
-
- if nil == rImage || nil == cImage {
- complention(-1)
- return
- }
-
- if (ImageProcess.checkEqualFor(resultPath, checkPath: checkPath)) {
- complention(100)
- return
- }
-
- let resultImage = rImage!
- let checkImage = cImage!
-
- var resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
- let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
-
- var rWidth = resultImageRep.pixelsWide
- var rHeight = resultImageRep.pixelsHigh
- // let rBitPerPixel = resultImageRep.bitsPerPixel / 8
- // let rBytePerRow = resultImageRep.bytesPerRow
-
- let cWidth = checkImageRep.pixelsWide
- let cHeight = checkImageRep.pixelsHigh
- // let cBitPerPixel = checkImageRep.bitsPerPixel / 8
- // let cBytePerRow = checkImageRep.bytesPerRow
-
- // 图像为等比例、且长宽比相同
- if rHeight*cHeight > 0 &&
- fabs(Double(rWidth-cWidth)) > 1 &&
- fabs(Double(rWidth)/Double(rHeight) - Double(cWidth)/Double(cHeight)) < 0.001 {
- var ciImage = CIImage(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
- ciImage = ciImage.transformed(by: CGAffineTransformMakeScale(Double(cWidth)/Double(rWidth), Double(cHeight)/Double(rHeight)))
- let context = CIContext()
- let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
- if cgImage != nil {
- resultImageRep = NSBitmapImageRep(cgImage: cgImage!);
-
- rWidth = resultImageRep.pixelsWide
- rHeight = resultImageRep.pixelsHigh
- }
- }
-
- let maxWidth = min(rWidth, cWidth) - 1
- let maxHeight = min(rHeight, cHeight) - 1
-
- if (maxWidth == 0 || maxHeight == 0) {
- complention(-1)
- return
- }
-
- // check background color
- // 挑选图片 对角斜线 上的相素进行识别
- let markInfo = NSMutableDictionary.init()
- for w in 0...maxWidth {
- let x = Int(w)
- for mark in 0...1 {
- let color = checkImageRep.colorAt(x: min(x, cWidth-1), y: min(mark == 0 ? x : (maxWidth - x), (cHeight-1))) as! NSColor
-
- if (markInfo[color] != nil) {
- let count = (markInfo[color] as? NSNumber)!.intValue
-
- markInfo[color] = NSNumber.init(value: count + 1)
- }else {
- markInfo[color] = NSNumber.init(value: 1)
- }
- }
- }
-
- var maxCount = Int(0);
- var bgColor : NSColor = NSColor.clear
- for color in markInfo.allKeys {
- let count = (markInfo[color] as? NSNumber)!.intValue
-
- if count > maxCount {
- maxCount = count
- bgColor = color as! NSColor
- }
- }
-
- // if nil != bgColor {
- // NSLog(String("识别到背景色\(bgColor)"))
- // }
-
- // let bg_r = Int(bgColor.redComponent*255);
- // let bg_g = Int(bgColor.redComponent*255);
- // let bg_b = Int(bgColor.redComponent*255);
- // let bg_a = Int(bgColor.redComponent*255);
- //
- let data = NSMutableData.init(length: cWidth * cHeight * 4)
- // Compare
- let compareDifValue = DataModel.shared.comparativeDifference()
- var equalCount = 0 as Double
- var bgCount = 0 as Double
-
- var processCount = Int(0)
- for w in 0...maxWidth {
- let x = Int(w)
-
- for h in 0...maxHeight {
- let y = Int(h)
-
- let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
- let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
-
- let cr = Int(cColor.redComponent*255)
- let cg = Int(cColor.greenComponent*255)
- let cb = Int(cColor.blueComponent*255)
- let ca = Int(cColor.blueComponent*255)
-
- let rr = Int(rColor.redComponent*255)
- let rg = Int(rColor.greenComponent*255)
- let rb = Int(rColor.blueComponent*255)
- let ra = Int(rColor.blueComponent*255)
-
- // if (cColor.isEqual(to: rColor)) {
- if (abs(cr - rr) <= compareDifValue &&
- abs(cg - rg) <= compareDifValue &&
- abs(cb - rb) <= compareDifValue &&
- abs(ca - ra) <= compareDifValue) {
- equalCount = equalCount + 1
-
- if cColor.isEqual(to: bgColor) {
- bgCount += 1
- }
- }else if (processCover && nil != data){
- let addr = cWidth * 4 * y + x * 4
-
- var r = uint8(255)
- data!.replaceBytes(in: NSRange.init(location: addr+0, length: 1), withBytes: &r)
- var g = uint8(255 * rColor.greenComponent/2)
- data!.replaceBytes(in: NSRange.init(location: addr+1, length: 1), withBytes: &g)
- var b = uint8(255 * rColor.blueComponent/2)
- data!.replaceBytes(in: NSRange.init(location: addr+2, length: 1), withBytes: &b)
- var a = uint8(255 * rColor.alphaComponent * 0.7)
- data!.replaceBytes(in: NSRange.init(location: addr+3, length: 1), withBytes: &a)
- }
- }
- }
-
- let outDegree = Double(max(equalCount-bgCount, 1)/(max(Double(cWidth) * Double(cHeight)-bgCount, 1)) * 100.0)
- if (abs(outDegree - 100) > 0 && processCover && nil != data) {
- DispatchQueue.global().async {
- autoreleasepool {
- let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
- let dataProvider = CGDataProvider.init(data: cfData!)
-
- let colorSpace = CGColorSpaceCreateDeviceRGB()
- let cgImage = CGImage.init(width: cWidth,
- height: cHeight,
- bitsPerComponent: 8,
- bitsPerPixel: 8 * 4,
- bytesPerRow: cWidth * 4,
- space: colorSpace,
- bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
- provider: dataProvider!,
- decode: nil,
- shouldInterpolate: true,
- intent: CGColorRenderingIntent.defaultIntent)
-
- if nil != cgImage {
- let coverPath = NSString(format: "%@_cover.png", NSString(string: resultPath).deletingPathExtension) as! String
- let rep = NSBitmapImageRep.init(cgImage: cgImage!)
-
- if let saveData = rep.representation(using: .png, properties: [:]) {
- let url = URL.init(fileURLWithPath: coverPath, isDirectory: false)
- try? saveData.write(to: url)
- }
- }
- }
- }
- }
-
- DispatchQueue.global().async {
- autoreleasepool {
- NSLog(String("过滤点数目\(bgCount)"))
-
- complention(outDegree)
- }
- }
- }
- }
-
- //Genera Image
- class func processImage(_ resultPath:String, checkPath:String) -> NSImage? {
- autoreleasepool {
- if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
- return nil
- }
-
- let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
- let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
-
- if nil == rImage || nil == cImage {
- return nil
- }
-
- if (ImageProcess.checkEqualFor(resultPath, checkPath: checkPath)) {
- return nil
- }
-
- let resultImage = rImage as! NSImage
- let checkImage = cImage as! NSImage
-
- let resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
- let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
-
- let rWidth = resultImageRep.pixelsWide
- let rHeight = resultImageRep.pixelsHigh
- let rBitPerPixel = resultImageRep.bitsPerPixel / 8
- let rBytePerRow = resultImageRep.bytesPerRow
-
- let cWidth = checkImageRep.pixelsWide
- let cHeight = checkImageRep.pixelsHigh
- let cBitPerPixel = checkImageRep.bitsPerPixel / 8
- let cBytePerRow = checkImageRep.bytesPerRow
- let data = NSMutableData.init(length: cWidth * cHeight * 4)
- if nil == data {
- return nil
- }
-
- let maxWidth = min(rWidth, cWidth) - 1
- let maxHeight = min(rHeight, cHeight) - 1
-
- // check background color
- // 挑选图片 对角斜线 上的相素进行识别
- var markInfo = NSMutableDictionary.init()
- for w in 0...maxWidth {
- let x = Int(w)
- for mark in 0...1 {
- let color = checkImageRep.colorAt(x: min(x, cWidth-1), y: min(mark == 0 ? x : (maxWidth - x), (cHeight-1))) as! NSColor
-
- if (markInfo[color] != nil) {
- let count = (markInfo[color] as? NSNumber)!.intValue ?? 0
-
- markInfo[color] = NSNumber.init(value: count + 1)
- }else {
- markInfo[color] = NSNumber.init(value: 1)
- }
- }
- }
-
- var maxCount = Int(0);
- var bgColor : NSColor? = nil
- for color in markInfo.allKeys {
- let count = (markInfo[color] as? NSNumber)!.intValue ?? 0
-
- if count > maxCount {
- maxCount = count
- bgColor = color as! NSColor
- }
- }
-
- if nil != bgColor {
- NSLog(String("识别到背景色\(bgColor)"))
- }
-
- // Compare
- let compareDifValue = DataModel.shared.comparativeDifference()
- for w in 0...maxWidth {
- let x = Int(w)
-
- for h in 0...maxHeight {
- let y = Int(h)
-
- let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
- let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
-
- let cr = Int(cColor.redComponent*255)
- let cg = Int(cColor.greenComponent*255)
- let cb = Int(cColor.blueComponent*255)
- let ca = Int(cColor.blueComponent*255)
-
- let rr = Int(rColor.redComponent*255)
- let rg = Int(rColor.greenComponent*255)
- let rb = Int(rColor.blueComponent*255)
- let ra = Int(rColor.blueComponent*255)
-
- // if (cColor.isEqual(to: rColor)) {
- if (abs(cr - rr) <= compareDifValue &&
- abs(cg - rg) <= compareDifValue &&
- abs(cb - rb) <= compareDifValue &&
- abs(ca - ra) <= compareDifValue) {
- if bgColor != nil &&
- cColor.isEqual(to: bgColor) {
- }
- }else {
- // NSLog("(\(cr),\(cg),\(cb),\(cr))=(\(rr),\(rg),\(rb),\(rr))")
- let addr = cWidth * 4 * y + x * 4
-
- var r = uint8(255)
- data!.replaceBytes(in: NSRange.init(location: addr+0, length: 1), withBytes: &r)
- var g = uint8(255 * rColor.greenComponent/2)
- data!.replaceBytes(in: NSRange.init(location: addr+1, length: 1), withBytes: &g)
- var b = uint8(255 * rColor.blueComponent/2)
- data!.replaceBytes(in: NSRange.init(location: addr+2, length: 1), withBytes: &b)
- var a = uint8(255 * rColor.alphaComponent * 0.7)
- data!.replaceBytes(in: NSRange.init(location: addr+3, length: 1), withBytes: &a)
- }
- }
- }
-
- let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
- let dataProvider = CGDataProvider.init(data: cfData!)
-
- let colorSpace = CGColorSpaceCreateDeviceRGB()
- let cgImage = CGImage.init(width: cWidth,
- height: cHeight,
- bitsPerComponent: 8,
- bitsPerPixel: 8 * 4,
- bytesPerRow: cWidth * 4,
- space: colorSpace,
- bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
- provider: dataProvider!,
- decode: nil,
- shouldInterpolate: true,
- intent: CGColorRenderingIntent.defaultIntent)
-
- if nil != cgImage {
- return NSImage.init(cgImage: cgImage!, size: NSSize.init(width: cWidth, height: cHeight))
- }
-
- return nil
- }
- }
-
- class func checkEqualFor(_ resultPath:String, checkPath:String) -> Bool {
- // 先通过文件属性 + MD5 码进行快速对比,排除大部分,已经相同的文件
- let rfs = try! FileManager.default.attributesOfItem(atPath: resultPath)[FileAttributeKey.size] as! NSNumber
- let cfs = try! FileManager.default.attributesOfItem(atPath: checkPath)[FileAttributeKey.size] as! NSNumber
- if rfs.int64Value != cfs.int64Value {
- //文件大小不一致,反回不一致
- return false
- }
-
- let rmd5 = ImageProcess.md5StringOfPath(resultPath);
- let cmd5 = ImageProcess.md5StringOfPath(checkPath);
-
- if (nil != rmd5 && nil != cmd5 &&
- NSString(string: rmd5!).isEqual(to: cmd5!)) {
- // 文件大小一致,且文件 MD5码完全一致
- return true;
- }
-
- return false
- }
-
- class func md5StringOfPath(_ path:String) -> String? {
- if FileManager.default.fileExists(atPath: path) {
- let data = NSData(contentsOfFile: path);
-
- if (nil == data) {
- return nil;
- }
- return String(format: "%X", data.hashValue);
- }
-
- return nil;
- }
-
- }
|