I’ve tried all the things together with ChatGPT and that i simply can not get my magic wand choice software written in swift to work. The one info I’ve discovered on StackOverflow has not been useful in any respect. And there doesn’t appear to be a library or code snippet on the market to get me shifting in the fitting path.
i am simply going to put up the entire class I am engaged on.
struct Coordinate: Hashable {
let x: Int
let y: Int
}
class MagicWandSelection: NSObject {
override init() {
tremendous.init()
}
func floodFill(x: Int, y: Int, width: Int, peak: Int, maskData: [UInt8], checked: inout [Bool]) -> [Coordinate] {
var stack = [Coordinate(x: x, y: y)]
var foundPixels = [Coordinate]()
whereas let level = stack.popLast() {
let index = level.y * width + level.x
if level.x < 0 || level.x >= width || level.y < 0 || level.y >= peak || checked[index] || maskData[index] != 255 {
proceed
}
checked[index] = true
foundPixels.append(level)
let instructions = [Coordinate(x: 0, y: -1), Coordinate(x: 1, y: 0), Coordinate(x: 0, y: 1), Coordinate(x: -1, y: 0)]
for path in instructions {
let nextPoint = Coordinate(x: level.x + path.x, y: level.y + path.y)
stack.append(nextPoint)
}
}
return foundPixels
}
func newMaskWithFloodFill(from level: CGPoint, in picture: UIImage, selectedColor: UIColor, tolerance: Float) -> UIImage? {
guard let cgImage = picture.cgImage else { return nil }
let width = cgImage.width
let peak = cgImage.peak
let bytesPerPixel = 4
let bytesPerRow = width * bytesPerPixel
let bitsPerComponent = 8
var pixelData = [UInt8](repeating: 0, rely: width * peak * bytesPerPixel)
guard let context = CGContext(information: &pixelData,
width: width,
peak: peak,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
area: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return nil }
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, peak: peak))
let startPixelIndex = (Int(level.y) * width + Int(level.x)) * bytesPerPixel
let startPixel = Array(pixelData[startPixelIndex..<(startPixelIndex + bytesPerPixel)])
var maskData = [UInt8](repeating: 0, rely: width * peak)
floodFill(&pixelData, &maskData, width: width, peak: peak, x: Int(level.x), y: Int(level.y), startPixel: startPixel, tolerance: tolerance)
return createMaskImage(from: maskData, width: width, peak: peak)
}
func floodFill(_ pixelData: inout [UInt8], _ maskData: inout [UInt8], width: Int, peak: Int, x: Int, y: Int, startPixel: [UInt8], tolerance: Float) {
var stack = [(Int, Int)]()
stack.append((x, y))
whereas !stack.isEmpty {
let (currentX, currentY) = stack.removeLast()
let index = (currentY * width + currentX) * 4
let maskIndex = currentY * width + currentX
if maskData[maskIndex] > 0 { proceed }
let currentPixel = Array(pixelData[index..<(index + 4)])
if !colorsMatch(pixel1: currentPixel, pixel2: startPixel, tolerance: tolerance) {
proceed
}
maskData[maskIndex] = 255
if currentX > 0 {
stack.append((currentX - 1, currentY))
}
if currentX < width - 1 {
stack.append((currentX + 1, currentY))
}
if currentY > 0 {
stack.append((currentX, currentY - 1))
}
if currentY < peak - 1 {
stack.append((currentX, currentY + 1))
}
}
}
func colorsMatch(pixel1: [UInt8], pixel2: [UInt8], tolerance: Float) -> Bool {
let r1 = Float(pixel1[0]) / 255.0, g1 = Float(pixel1[1]) / 255.0, b1 = Float(pixel1[2]) / 255.0, a1 = Float(pixel1[3]) / 255.0
let r2 = Float(pixel2[0]) / 255.0, g2 = Float(pixel2[1]) / 255.0, b2 = Float(pixel2[2]) / 255.0, a2 = Float(pixel2[3]) / 255.0
let dr = r1 - r2, dg = g1 - g2, db = b1 - b2, da = a1 - a2
let distance = sqrt(dr * dr + dg * dg + db * db + da * da)
return distance < tolerance
}
func createMaskImage(from maskData: [UInt8], width: Int, peak: Int) -> UIImage? {
let bytesPerPixel = 1
let bytesPerRow = width * bytesPerPixel
let bitsPerComponent = 8
var maskDataModified = maskData.map { $0 > 0 ? UInt8(255) : UInt8(0) }
guard let context = CGContext(information: &maskDataModified,
width: width,
peak: peak,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
area: CGColorSpaceCreateDeviceGray(),
bitmapInfo: CGImageAlphaInfo.none.rawValue) else { return nil }
guard let maskCgImage = context.makeImage() else { return nil }
return UIImage(cgImage: maskCgImage)
}
func createBezierPath(from maskImage: UIImage, seed: CGPoint) -> UIBezierPath? {
guard let cgImage = maskImage.cgImage else { return nil }
let width = Int(maskImage.measurement.width)
let peak = Int(maskImage.measurement.peak)
let bytesPerPixel = 1
let bytesPerRow = width * bytesPerPixel
let bitsPerComponent = 8
var maskData = [UInt8](repeating: 0, rely: width * peak)
guard let context = CGContext(information: &maskData,
width: width,
peak: peak,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
area: CGColorSpaceCreateDeviceGray(),
bitmapInfo: CGImageAlphaInfo.none.rawValue) else { return nil }
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, peak: peak))
let path = UIBezierPath()
let seedPoint = Coordinate(x: Int(seed.x), y: Int(seed.y))
// Make sure the seed level is inside bounds and on a white pixel
guard seedPoint.x >= 0 && seedPoint.x < width && seedPoint.y >= 0 && seedPoint.y < peak else { return nil }
guard maskData[seedPoint.y * width + seedPoint.x] == 255 else { return nil }
var currentPoint = seedPoint
let instructions = [
Coordinate(x: 0, y: -1), // Up
Coordinate(x: 1, y: 0), // Right
Coordinate(x: 0, y: 1), // Down
Coordinate(x: -1, y: 0) // Left
]
// Discover the beginning edge pixel by shifting up and left from the seed pixel
var startPoint: Coordinate? = nil
for path in [Coordinate(x: 0, y: -1), Coordinate(x: -1, y: 0)] {
var testPoint = seedPoint
whereas testPoint.x >= 0 && testPoint.y >= 0 {
if maskData[testPoint.y * width + testPoint.x] == 255 && isEdgePixel(x: testPoint.x, y: testPoint.y, width: width, peak: peak, maskData: maskData) {
startPoint = testPoint
break
}
testPoint = Coordinate(x: testPoint.x + path.x, y: testPoint.y + path.y)
}
if startPoint != nil {
break
}
}
guard let firstPoint = startPoint else { return nil }
currentPoint = firstPoint
path.transfer(to: CGPoint(x: firstPoint.x, y: firstPoint.y))
var visited: Set<Coordinate> = [firstPoint]
repeat {
var foundNext = false
for path in instructions {
let nextPoint = Coordinate(x: currentPoint.x + path.x, y: currentPoint.y + path.y)
let nextIndex = nextPoint.y * width + nextPoint.x
if nextPoint.x >= 0 && nextPoint.x < width && nextPoint.y >= 0 && nextPoint.y < peak &&
maskData[nextIndex] == 255 &&
!visited.comprises(nextPoint) &&
isEdgePixel(x: nextPoint.x, y: nextPoint.y, width: width, peak: peak, maskData: maskData) {
path.addLine(to: CGPoint(x: nextPoint.x, y: nextPoint.y))
visited.insert(nextPoint)
currentPoint = nextPoint
foundNext = true
break
}
}
if !foundNext {
break
}
} whereas currentPoint != firstPoint
path.shut()
return path
}
}
extension MagicWandSelection{
func isEdgePixel(x: Int, y: Int, width: Int, peak: Int, maskData: [UInt8]) -> Bool {
if x == 0 || y == 0 || x == width - 1 || y == peak - 1 {
return true
}
let instructions = [
Coordinate(x: 0, y: -1), // Up
Coordinate(x: 1, y: 0), // Right
Coordinate(x: 0, y: 1), // Down
Coordinate(x: -1, y: 0) // Left
]
for path in instructions {
let neighborX = x + path.x
let neighborY = y + path.y
if neighborX >= 0 && neighborX < width && neighborY >= 0 && neighborY < peak {
let neighborIndex = neighborY * width + neighborX
if maskData[neighborIndex] == 0 {
return true
}
}
}
return false
}
func createMagicWandPath(from maskImage: UIImage, seed: CGPoint) -> UIBezierPath? {
guard let cgImage = maskImage.cgImage else { return nil }
let width = Int(maskImage.measurement.width)
let peak = Int(maskImage.measurement.peak)
let bytesPerPixel = 1
let bytesPerRow = width * bytesPerPixel
let bitsPerComponent = 8
var maskData = [UInt8](repeating: 0, rely: width * peak)
guard let context = CGContext(information: &maskData,
width: width,
peak: peak,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
area: CGColorSpaceCreateDeviceGray(),
bitmapInfo: CGImageAlphaInfo.none.rawValue) else { return nil }
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, peak: peak))
let seedPoint = Coordinate(x: Int(seed.x), y: Int(seed.y))
// Make sure the seed level is inside bounds and on a white pixel
guard seedPoint.x >= 0 && seedPoint.x < width && seedPoint.y >= 0 && seedPoint.y < peak else { return nil }
guard maskData[seedPoint.y * width + seedPoint.x] == 255 else { return nil }
// Discover the beginning edge pixel
guard let startPixel = findStartingEdgePixel(from: seedPoint, width: width, peak: peak, maskData: maskData) else {
return nil
}
// Discover edge pixels and assemble the trail
if let edgePath = traceEdgePath(from: startPixel, width: width, peak: peak, maskData: maskData) {
return edgePath
}
return nil
}
func findStartingEdgePixel(from seedPoint: Coordinate, width: Int, peak: Int, maskData: [UInt8]) -> Coordinate? {
for y in 0..<peak {
for x in 0..<width {
let index = y * width + x
if maskData[index] == 255 && isEdgePixel(x: x, y: y, width: width, peak: peak, maskData: maskData) {
return Coordinate(x: x, y: y)
}
}
}
return nil
}
func traceEdgePath(from startPoint: Coordinate, width: Int, peak: Int, maskData: [UInt8]) -> UIBezierPath? {
let instructions = [
Coordinate(x: 0, y: -1), // Up
Coordinate(x: 1, y: 0), // Right
Coordinate(x: 0, y: 1), // Down
Coordinate(x: -1, y: 0), // Left
Coordinate(x: -1, y: -1), // Up-Left
Coordinate(x: 1, y: -1), // Up-Right
Coordinate(x: -1, y: 1), // Down-Left
Coordinate(x: 1, y: 1) // Down-Right
]
let path = UIBezierPath()
var visited = Set<Coordinate>()
var currentPoint = startPoint
var firstPoint = true
repeat {
visited.insert(currentPoint)
if firstPoint {
path.transfer(to: CGPoint(x: currentPoint.x, y: currentPoint.y))
firstPoint = false
} else {
path.addLine(to: CGPoint(x: currentPoint.x, y: currentPoint.y))
}
var foundNext = false
for path in instructions {
let nextPoint = Coordinate(x: currentPoint.x + path.x, y: currentPoint.y + path.y)
if nextPoint.x >= 0 && nextPoint.x < width && nextPoint.y >= 0 && nextPoint.y < peak &&
maskData[nextPoint.y * width + nextPoint.x] == 255 && !visited.comprises(nextPoint) {
currentPoint = nextPoint
foundNext = true
break
}
}
if !foundNext {
break
}
} whereas currentPoint != startPoint
path.shut()
return path
}
}
I’ve tried just about all the things I can consider together with making an attempt to make use of chatgpt to repair my code. What I get is a large number of a variety with the UIBezierPath zigzaging the picture.
I used the ! right here in order that it will crash if nothing is returned this can be fastened in manufacturing code.
that is the utilization instance for the above code:
let magicWand = MagicWandSelection()
let coloration = UIColor.white
let pixel = GPPixel(x: Int(pt.x), y: Int(pt.y), coloration: coloration)
let tolerance: Float = 0.1
let img = magicWand.newMaskWithFloodFill(from: pt, in: self.picture, selectedColor: coloration, tolerance: tolerance)!
self.selectionPath = magicWand.createMagicWandPath(from: img, seed: pt)
the floodFill masks returns and picture of a black background with the pixels that needs to be outlined with a variety path as white pixels. Which ought to make this simple to determine however I simply can not get previous the place i’m.