Saturday, June 1, 2024
HomeiOS Developmentios - Find out how to create a Magic Wand choice software...

ios – Find out how to create a Magic Wand choice software like photoshop


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.



Supply hyperlink

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments