Обрезка CIImage

У меня есть класс, который принимает UIImage, инициализирует с ним CIImage вот так:

workingImage = CIImage.init(image: baseImage!)

Затем изображение используется для вырезания из него 9 соседних квадратов по шаблону 3х3 - в цикле:

for x in 0..<3
    {
        for y in 0..<3
        {

            croppingRect = CGRect(x: CGFloat(Double(x) * sideLength + startPointX),
                                  y: CGFloat(Double(y) * sideLength + startPointY),
                                  width: CGFloat(sideLength),
                                  height: CGFloat(sideLength))
            let tmpImg = (workingImage?.cropping(to: croppingRect))!
        }
    }

Эти tmpImgs вставляются в таблицу и позже используются, но это не главное.

Этот код работает на симуляторах IOS 9 и IOS 10, но не на реальном устройстве iOS 10. Произведенные изображения либо все пусты, либо одно из них похоже на половину того, что должно быть, а остальные, опять же, пусты.

Разве это не то, как это должно быть сделано в IOS 10?


person VID44R    schedule 24.11.2016    source источник
comment
Ясно, что все зависит от правильного и значимого значения для sideLength, startPointX и startPointY. Но вы не дали ни малейшего представления о том, как вы получаете эти важные числа. (Я также должен упомянуть, что такие явления, как работа на симуляторе, но не на устройстве, часто вызваны проблемами с потоками.)   -  person matt    schedule 24.11.2016
comment
Извините - еще одно. Почему вы вообще проходите через CIImage? Я немного беспокоюсь, что это потому, что вы думаете, что это способ обрезать UIImage, а это не так. Если у вас нет особого применения для CIImage, то есть если вы не собираетесь применять CIFilter, не используйте его вообще!   -  person matt    schedule 24.11.2016
comment
Этот код не обрезает workingImage. Обрезанное изображение назначается tmpImg, но сразу же освобождается, поскольку переменная никогда не используется. Итак, workingImage не меняется. Это настоящий код?   -  person Codo    schedule 25.11.2016
comment
@matt, на самом деле, я действительно думаю, что это способ обрезать изображение. Я не знаю никакого другого метода, так как большинство вещей, которые я нахожу в Интернете, связанных с кадрированием, либо вообще не работают, либо используют CIImage. Я очень ценю, если вы указали мне правильный путь.   -  person VID44R    schedule 26.11.2016
comment
@Codo, tmpImg добавляется в массив изображений в цикле, я пропустил это, потому что не думаю, что это имеет значение в данном случае. Как я уже сказал, этот метод работает корректно на IOS9 устройствах, что еще больше меня смущает.   -  person VID44R    schedule 26.11.2016
comment
@ VID44R Хорошо, я показал вам, как это сделать правильно (и надежно).   -  person matt    schedule 26.11.2016


Ответы (2)


Суть дела в том, что прохождение через CIImage — это не способ обрезать UIImage. Во-первых, возвращение из CIImage в UIImage — сложный процесс. Во-вторых, весь путь туда и обратно не нужен.

Как обрезать

Чтобы обрезать изображение, создайте графический контекст изображения нужного обрезанного размера и вызовите draw(at:) в UIImage, чтобы отрисовать его в нужной точке относительно графического контекста, чтобы нужная часть изображения попала в контекст. Теперь извлеките полученное новое изображение и закройте контекст.

Чтобы продемонстрировать, я обрезаю одну треть, до которой вы пытаетесь обрезать, а именно нижнюю правую треть:

let sz = baseImage.size
UIGraphicsBeginImageContextWithOptions(
    CGSize(width:sz.width/3.0, height:sz.height/3.0), 
    false, 0)
baseImage.draw(at:CGPoint(x: -sz.width/3.0*2.0, y: -sz.height/3.0*2.0))
let tmpImg = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Исходное изображение (baseImage):

введите описание изображения здесь

Обрезанное изображение (tmpImg):

введите описание изображения здесь

Остальные участки полностью параллельны.

person matt    schedule 26.11.2016

Система координат Core Image не соответствует UIKit, поэтому нужно отразить прямоугольник.

Итак, в вашем конкретном случае вы хотите:

var ciRect = croppingRect
ciRect.origin.y = workingImage!.extent.height - ciRect.origin.y - ciRect.height
let tmpImg = workingImage!.cropped(to: ciRect)

Это определенно работает для iOS 10+.

В более общем случае мы бы сделали расширение UIImage, которое охватывает обе возможные системы координат, и это намного быстрее, чем draw(at:):

extension UIImage {
    /// Return a new image cropped to a rectangle.
    /// - parameter rect:
    /// The rectangle to crop.
    open func cropped(to rect: CGRect) -> UIImage {
        // a UIImage is either initialized using a CGImage, a CIImage, or nothing
        if let cgImage = self.cgImage {
            // CGImage.cropping(to:) is magnitudes faster than UIImage.draw(at:)
            if let cgCroppedImage = cgImage.cropping(to: rect) {
                return UIImage(cgImage: cgCroppedImage)
            } else {
                return UIImage()
            }
        }
        if let ciImage = self.ciImage {
            // Core Image's coordinate system mismatch with UIKit, so rect needs to be mirrored.
            var ciRect = rect
            ciRect.origin.y = ciImage.extent.height - ciRect.origin.y - ciRect.height
            let ciCroppedImage = ciImage.cropped(to: ciRect)
            return UIImage(ciImage: ciCroppedImage)
        }
        return self
    }
}

Я сделал для него модуль, поэтому исходный код находится по адресу https://github.com/Coeur/ImageEffects/blob/master/SwiftImageEffects/ImageEffects%2Bextensions.swift

person Cœur    schedule 17.06.2019