ImageProcess.swift 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. //
  2. // ImageProcess.swift
  3. // KdanAuto
  4. //
  5. // Created by 朱东勇 on 2023/2/7.
  6. //
  7. import Foundation
  8. import AppKit
  9. import CoreImage
  10. class ImageProcess : NSObject {
  11. // Image compare
  12. class func compareJPEG(_ resultPath:String, checkPath:String, processCover:Bool) -> Double {
  13. autoreleasepool {
  14. if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
  15. return -1
  16. }
  17. let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
  18. let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
  19. if nil == rImage || nil == cImage {
  20. return -1
  21. }
  22. let resultImage = rImage!
  23. let checkImage = cImage!
  24. var resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  25. let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  26. var rWidth = resultImageRep.pixelsWide
  27. var rHeight = resultImageRep.pixelsHigh
  28. // let rBitPerPixel = resultImageRep.bitsPerPixel / 8
  29. // let rBytePerRow = resultImageRep.bytesPerRow
  30. let cWidth = checkImageRep.pixelsWide
  31. let cHeight = checkImageRep.pixelsHigh
  32. // let cBitPerPixel = checkImageRep.bitsPerPixel / 8
  33. // let cBytePerRow = checkImageRep.bytesPerRow
  34. // 图像为等比例、且长宽比相同
  35. if rHeight*cHeight > 0 &&
  36. fabs(Double(rWidth-cWidth)) > 1 &&
  37. fabs(Double(rWidth)/Double(rHeight) - Double(cWidth)/Double(cHeight)) < 0.001 {
  38. var ciImage = CIImage(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  39. ciImage = ciImage.transformed(by: CGAffineTransformMakeScale(Double(cWidth)/Double(rWidth), Double(cHeight)/Double(rHeight)))
  40. let context = CIContext()
  41. let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
  42. if cgImage != nil {
  43. resultImageRep = NSBitmapImageRep(cgImage: cgImage!);
  44. rWidth = resultImageRep.pixelsWide
  45. rHeight = resultImageRep.pixelsHigh
  46. }
  47. }
  48. let maxWidth = min(rWidth, cWidth) - 1
  49. let maxHeight = min(rHeight, cHeight) - 1
  50. // check background color
  51. // 挑选图片 对角斜线 上的相素进行识别
  52. let markInfo = NSMutableDictionary.init()
  53. for w in 0...maxWidth {
  54. let x = Int(w)
  55. for mark in 0...1 {
  56. let color = checkImageRep.colorAt(x: min(x, cWidth-1), y: min(mark == 0 ? x : (maxWidth - x), (cHeight-1))) as! NSColor
  57. if (markInfo[color] != nil) {
  58. let count = (markInfo[color] as? NSNumber)!.intValue
  59. markInfo[color] = NSNumber.init(value: count + 1)
  60. }else {
  61. markInfo[color] = NSNumber.init(value: 1)
  62. }
  63. }
  64. }
  65. var maxCount = Int(0);
  66. var bgColor : NSColor = NSColor.clear
  67. for color in markInfo.allKeys {
  68. let count = (markInfo[color] as? NSNumber)!.intValue
  69. if count > maxCount {
  70. maxCount = count
  71. bgColor = color as! NSColor
  72. }
  73. }
  74. // if nil != bgColor {
  75. // NSLog(String("识别到背景色\(bgColor)"))
  76. // }
  77. // let bg_r = Int(bgColor.redComponent*255);
  78. // let bg_g = Int(bgColor.redComponent*255);
  79. // let bg_b = Int(bgColor.redComponent*255);
  80. // let bg_a = Int(bgColor.redComponent*255);
  81. //
  82. let data = NSMutableData.init(length: cWidth * cHeight * 4)
  83. // Compare
  84. let compareDifValue = DataModel.shared.comparativeDifference()
  85. var equalCount = 0 as Double
  86. var bgCount = 0 as Double
  87. let semaphore = DispatchSemaphore(value: 0)
  88. let operateQueue = OperationQueue()
  89. operateQueue.maxConcurrentOperationCount = 20;
  90. var processCount = Int(0)
  91. for w in 0...maxWidth {
  92. let x = Int(w)
  93. operateQueue.addOperation {
  94. for h in 0...maxHeight {
  95. let y = Int(h)
  96. objc_sync_enter(self)
  97. let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
  98. let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
  99. objc_sync_exit(self)
  100. let cr = Int(cColor.redComponent*255)
  101. let cg = Int(cColor.greenComponent*255)
  102. let cb = Int(cColor.blueComponent*255)
  103. let ca = Int(cColor.blueComponent*255)
  104. let rr = Int(rColor.redComponent*255)
  105. let rg = Int(rColor.greenComponent*255)
  106. let rb = Int(rColor.blueComponent*255)
  107. let ra = Int(rColor.blueComponent*255)
  108. // if (cColor.isEqual(to: rColor)) {
  109. if (abs(cr - rr) <= compareDifValue &&
  110. abs(cg - rg) <= compareDifValue &&
  111. abs(cb - rb) <= compareDifValue &&
  112. abs(ca - ra) <= compareDifValue) {
  113. equalCount = equalCount + 1
  114. if cColor.isEqual(to: bgColor) {
  115. objc_sync_enter(self)
  116. bgCount += 1
  117. objc_sync_exit(self)
  118. }
  119. }else if (processCover && nil != data){
  120. let addr = cWidth * 4 * y + x * 4
  121. objc_sync_enter(self)
  122. var r = uint8(255)
  123. data!.replaceBytes(in: NSRange.init(location: addr+0, length: 1), withBytes: &r)
  124. var g = uint8(255 * rColor.greenComponent/2)
  125. data!.replaceBytes(in: NSRange.init(location: addr+1, length: 1), withBytes: &g)
  126. var b = uint8(255 * rColor.blueComponent/2)
  127. data!.replaceBytes(in: NSRange.init(location: addr+2, length: 1), withBytes: &b)
  128. var a = uint8(255 * rColor.alphaComponent * 0.7)
  129. data!.replaceBytes(in: NSRange.init(location: addr+3, length: 1), withBytes: &a)
  130. objc_sync_exit(self)
  131. }
  132. }
  133. processCount = processCount+1
  134. if (processCount >= maxWidth) {
  135. semaphore.signal()
  136. }
  137. }
  138. }
  139. semaphore.wait()
  140. let outDegree = Double(max(equalCount-bgCount, 1)/(max(Double(cWidth) * Double(cHeight)-bgCount, 1)) * 100.0)
  141. if (abs(outDegree - 100) > 0 && processCover && nil != data) {
  142. DispatchQueue.global().async {
  143. let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
  144. let dataProvider = CGDataProvider.init(data: cfData!)
  145. let colorSpace = CGColorSpaceCreateDeviceRGB()
  146. let cgImage = CGImage.init(width: cWidth,
  147. height: cHeight,
  148. bitsPerComponent: 8,
  149. bitsPerPixel: 8 * 4,
  150. bytesPerRow: cWidth * 4,
  151. space: colorSpace,
  152. bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
  153. provider: dataProvider!,
  154. decode: nil,
  155. shouldInterpolate: true,
  156. intent: CGColorRenderingIntent.defaultIntent)
  157. if nil != cgImage {
  158. let coverPath = NSString(format: "%@_cover.png", NSString(string: resultPath).deletingPathExtension) as! String
  159. let rep = NSBitmapImageRep.init(cgImage: cgImage!)
  160. if let saveData = rep.representation(using: .png, properties: [:]) {
  161. let url = URL.init(fileURLWithPath: coverPath, isDirectory: false)
  162. try? saveData.write(to: url)
  163. }
  164. }
  165. }
  166. }
  167. NSLog(String("过滤点数目\(bgCount)"))
  168. return outDegree
  169. }
  170. }
  171. class func compareJPEG(_ resultPath:String, checkPath:String, processCover:Bool, complention:@escaping (_ degree:Double) -> ()) {
  172. autoreleasepool {
  173. if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
  174. complention(-1)
  175. return
  176. }
  177. let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
  178. let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
  179. if nil == rImage || nil == cImage {
  180. complention(-1)
  181. return
  182. }
  183. let resultImage = rImage!
  184. var checkImage = cImage!
  185. var resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  186. let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  187. var rWidth = resultImageRep.pixelsWide
  188. var rHeight = resultImageRep.pixelsHigh
  189. // let rBitPerPixel = resultImageRep.bitsPerPixel / 8
  190. // let rBytePerRow = resultImageRep.bytesPerRow
  191. let cWidth = checkImageRep.pixelsWide
  192. let cHeight = checkImageRep.pixelsHigh
  193. // let cBitPerPixel = checkImageRep.bitsPerPixel / 8
  194. // let cBytePerRow = checkImageRep.bytesPerRow
  195. // 图像为等比例、且长宽比相同
  196. if rHeight*cHeight > 0 &&
  197. fabs(Double(rWidth-cWidth)) > 1 &&
  198. fabs(Double(rWidth)/Double(rHeight) - Double(cWidth)/Double(cHeight)) < 0.001 {
  199. var ciImage = CIImage(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  200. ciImage = ciImage.transformed(by: CGAffineTransformMakeScale(Double(cWidth)/Double(rWidth), Double(cHeight)/Double(rHeight)))
  201. let context = CIContext()
  202. let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
  203. if cgImage != nil {
  204. resultImageRep = NSBitmapImageRep(cgImage: cgImage!);
  205. rWidth = resultImageRep.pixelsWide
  206. rHeight = resultImageRep.pixelsHigh
  207. }
  208. }
  209. let maxWidth = min(rWidth, cWidth) - 1
  210. let maxHeight = min(rHeight, cHeight) - 1
  211. if (maxWidth == 0 || maxHeight == 0) {
  212. complention(-1)
  213. return
  214. }
  215. // check background color
  216. // 挑选图片 对角斜线 上的相素进行识别
  217. let markInfo = NSMutableDictionary.init()
  218. for w in 0...maxWidth {
  219. let x = Int(w)
  220. for mark in 0...1 {
  221. let color = checkImageRep.colorAt(x: min(x, cWidth-1), y: min(mark == 0 ? x : (maxWidth - x), (cHeight-1))) as! NSColor
  222. if (markInfo[color] != nil) {
  223. let count = (markInfo[color] as? NSNumber)!.intValue
  224. markInfo[color] = NSNumber.init(value: count + 1)
  225. }else {
  226. markInfo[color] = NSNumber.init(value: 1)
  227. }
  228. }
  229. }
  230. var maxCount = Int(0);
  231. var bgColor : NSColor = NSColor.clear
  232. for color in markInfo.allKeys {
  233. let count = (markInfo[color] as? NSNumber)!.intValue
  234. if count > maxCount {
  235. maxCount = count
  236. bgColor = color as! NSColor
  237. }
  238. }
  239. // if nil != bgColor {
  240. // NSLog(String("识别到背景色\(bgColor)"))
  241. // }
  242. // let bg_r = Int(bgColor.redComponent*255);
  243. // let bg_g = Int(bgColor.redComponent*255);
  244. // let bg_b = Int(bgColor.redComponent*255);
  245. // let bg_a = Int(bgColor.redComponent*255);
  246. //
  247. let data = NSMutableData.init(length: cWidth * cHeight * 4)
  248. // Compare
  249. let compareDifValue = DataModel.shared.comparativeDifference()
  250. var equalCount = 0 as Double
  251. var bgCount = 0 as Double
  252. var processCount = Int(0)
  253. for w in 0...maxWidth {
  254. let x = Int(w)
  255. for h in 0...maxHeight {
  256. let y = Int(h)
  257. let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
  258. let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
  259. let cr = Int(cColor.redComponent*255)
  260. let cg = Int(cColor.greenComponent*255)
  261. let cb = Int(cColor.blueComponent*255)
  262. let ca = Int(cColor.blueComponent*255)
  263. let rr = Int(rColor.redComponent*255)
  264. let rg = Int(rColor.greenComponent*255)
  265. let rb = Int(rColor.blueComponent*255)
  266. let ra = Int(rColor.blueComponent*255)
  267. // if (cColor.isEqual(to: rColor)) {
  268. if (abs(cr - rr) <= compareDifValue &&
  269. abs(cg - rg) <= compareDifValue &&
  270. abs(cb - rb) <= compareDifValue &&
  271. abs(ca - ra) <= compareDifValue) {
  272. equalCount = equalCount + 1
  273. if cColor.isEqual(to: bgColor) {
  274. bgCount += 1
  275. }
  276. }else if (processCover && nil != data){
  277. let addr = cWidth * 4 * y + x * 4
  278. var r = uint8(255)
  279. data!.replaceBytes(in: NSRange.init(location: addr+0, length: 1), withBytes: &r)
  280. var g = uint8(255 * rColor.greenComponent/2)
  281. data!.replaceBytes(in: NSRange.init(location: addr+1, length: 1), withBytes: &g)
  282. var b = uint8(255 * rColor.blueComponent/2)
  283. data!.replaceBytes(in: NSRange.init(location: addr+2, length: 1), withBytes: &b)
  284. var a = uint8(255 * rColor.alphaComponent * 0.7)
  285. data!.replaceBytes(in: NSRange.init(location: addr+3, length: 1), withBytes: &a)
  286. }
  287. }
  288. }
  289. let outDegree = Double(max(equalCount-bgCount, 1)/(max(Double(cWidth) * Double(cHeight)-bgCount, 1)) * 100.0)
  290. if (abs(outDegree - 100) > 0 && processCover && nil != data) {
  291. DispatchQueue.global().async {
  292. let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
  293. let dataProvider = CGDataProvider.init(data: cfData!)
  294. let colorSpace = CGColorSpaceCreateDeviceRGB()
  295. let cgImage = CGImage.init(width: cWidth,
  296. height: cHeight,
  297. bitsPerComponent: 8,
  298. bitsPerPixel: 8 * 4,
  299. bytesPerRow: cWidth * 4,
  300. space: colorSpace,
  301. bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
  302. provider: dataProvider!,
  303. decode: nil,
  304. shouldInterpolate: true,
  305. intent: CGColorRenderingIntent.defaultIntent)
  306. if nil != cgImage {
  307. let coverPath = NSString(format: "%@_cover.png", NSString(string: resultPath).deletingPathExtension) as! String
  308. let rep = NSBitmapImageRep.init(cgImage: cgImage!)
  309. if let saveData = rep.representation(using: .png, properties: [:]) {
  310. let url = URL.init(fileURLWithPath: coverPath, isDirectory: false)
  311. try? saveData.write(to: url)
  312. }
  313. }
  314. }
  315. }
  316. DispatchQueue.global().async {
  317. NSLog(String("过滤点数目\(bgCount)"))
  318. complention(outDegree)
  319. }
  320. }
  321. }
  322. //Genera Image
  323. class func processImage(_ resultPath:String, checkPath:String) -> NSImage? {
  324. autoreleasepool {
  325. if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
  326. return nil
  327. }
  328. let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
  329. let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
  330. if nil == rImage || nil == cImage {
  331. return nil
  332. }
  333. let resultImage = rImage as! NSImage
  334. let checkImage = cImage as! NSImage
  335. let resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  336. let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
  337. let rWidth = resultImageRep.pixelsWide
  338. let rHeight = resultImageRep.pixelsHigh
  339. let rBitPerPixel = resultImageRep.bitsPerPixel / 8
  340. let rBytePerRow = resultImageRep.bytesPerRow
  341. let cWidth = checkImageRep.pixelsWide
  342. let cHeight = checkImageRep.pixelsHigh
  343. let cBitPerPixel = checkImageRep.bitsPerPixel / 8
  344. let cBytePerRow = checkImageRep.bytesPerRow
  345. let data = NSMutableData.init(length: cWidth * cHeight * 4)
  346. if nil == data {
  347. return nil
  348. }
  349. let maxWidth = min(rWidth, cWidth) - 1
  350. let maxHeight = min(rHeight, cHeight) - 1
  351. // check background color
  352. // 挑选图片 对角斜线 上的相素进行识别
  353. var markInfo = NSMutableDictionary.init()
  354. for w in 0...maxWidth {
  355. let x = Int(w)
  356. for mark in 0...1 {
  357. let color = checkImageRep.colorAt(x: min(x, cWidth-1), y: min(mark == 0 ? x : (maxWidth - x), (cHeight-1))) as! NSColor
  358. if (markInfo[color] != nil) {
  359. let count = (markInfo[color] as? NSNumber)!.intValue ?? 0
  360. markInfo[color] = NSNumber.init(value: count + 1)
  361. }else {
  362. markInfo[color] = NSNumber.init(value: 1)
  363. }
  364. }
  365. }
  366. var maxCount = Int(0);
  367. var bgColor : NSColor? = nil
  368. for color in markInfo.allKeys {
  369. let count = (markInfo[color] as? NSNumber)!.intValue ?? 0
  370. if count > maxCount {
  371. maxCount = count
  372. bgColor = color as! NSColor
  373. }
  374. }
  375. if nil != bgColor {
  376. NSLog(String("识别到背景色\(bgColor)"))
  377. }
  378. // Compare
  379. let compareDifValue = DataModel.shared.comparativeDifference()
  380. for w in 0...maxWidth {
  381. let x = Int(w)
  382. for h in 0...maxHeight {
  383. let y = Int(h)
  384. let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
  385. let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
  386. let cr = Int(cColor.redComponent*255)
  387. let cg = Int(cColor.greenComponent*255)
  388. let cb = Int(cColor.blueComponent*255)
  389. let ca = Int(cColor.blueComponent*255)
  390. let rr = Int(rColor.redComponent*255)
  391. let rg = Int(rColor.greenComponent*255)
  392. let rb = Int(rColor.blueComponent*255)
  393. let ra = Int(rColor.blueComponent*255)
  394. // if (cColor.isEqual(to: rColor)) {
  395. if (abs(cr - rr) <= compareDifValue &&
  396. abs(cg - rg) <= compareDifValue &&
  397. abs(cb - rb) <= compareDifValue &&
  398. abs(ca - ra) <= compareDifValue) {
  399. if bgColor != nil &&
  400. cColor.isEqual(to: bgColor) {
  401. }
  402. }else {
  403. // NSLog("(\(cr),\(cg),\(cb),\(cr))=(\(rr),\(rg),\(rb),\(rr))")
  404. let addr = cWidth * 4 * y + x * 4
  405. var r = uint8(255)
  406. data!.replaceBytes(in: NSRange.init(location: addr+0, length: 1), withBytes: &r)
  407. var g = uint8(255 * rColor.greenComponent/2)
  408. data!.replaceBytes(in: NSRange.init(location: addr+1, length: 1), withBytes: &g)
  409. var b = uint8(255 * rColor.blueComponent/2)
  410. data!.replaceBytes(in: NSRange.init(location: addr+2, length: 1), withBytes: &b)
  411. var a = uint8(255 * rColor.alphaComponent * 0.7)
  412. data!.replaceBytes(in: NSRange.init(location: addr+3, length: 1), withBytes: &a)
  413. }
  414. }
  415. }
  416. let cfData = CFDataCreate(kCFAllocatorDefault, data?.bytes, data!.length)
  417. let dataProvider = CGDataProvider.init(data: cfData!)
  418. let colorSpace = CGColorSpaceCreateDeviceRGB()
  419. let cgImage = CGImage.init(width: cWidth,
  420. height: cHeight,
  421. bitsPerComponent: 8,
  422. bitsPerPixel: 8 * 4,
  423. bytesPerRow: cWidth * 4,
  424. space: colorSpace,
  425. bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) ?? CGBitmapInfo.byteOrderDefault,
  426. provider: dataProvider!,
  427. decode: nil,
  428. shouldInterpolate: true,
  429. intent: CGColorRenderingIntent.defaultIntent)
  430. if nil != cgImage {
  431. return NSImage.init(cgImage: cgImage!, size: NSSize.init(width: cWidth, height: cHeight))
  432. }
  433. return nil
  434. }
  435. }
  436. }