Post

Replies

Boosts

Views

Activity

Reply to EDR doesn't work on iOS?
To enable EDR rendering, all we do is to set the colorPixelFormat to MTLPixelFormatRGBA16Float (note: RGBA, not BGRA) and wantsExtendedDynamicRangeContent to YES. We don't change the colorSpace since it is already set to extended linear sRGB when setting the other properties. As soon as we render pixel values outside [0...1], the screen switches to EDR mode and the potential and current HDR headroom adjust accordingly.
Topic: Graphics & Games SubTopic: Metal Tags:
Mar ’23
Reply to CIFilter documentation for CIMaximumComponent?
For Core Image documentation in general, I can recommend cifilter.io, though it does not list the newest filters. You can also check out the Filter Magic app, which lets you play with most CIFilters and has a lot of documentation. As for CIMaximumComponent and CIMinimumComponent: They will take the max/min values of R, G, and B and return a pixel with all channels set to this value. Some examples: RGB(1.0, 0.0, 0.0) -> max: RGB(1.0, 1.0, 1.0) | min: RGB(0.0, 0.0, 0.0) RGB(0.5, 0.7, 0.3) -> max: RGB(0.7, 0.7, 0.7) | min: RGB(0.3, 0.3, 0.3) So yes, they turn the image into grayscale, but I might not be what you want since the value doesn't represent perceived lightness of the color. You might want to check out CIPhotoEffectMono, CIPhotoEffectNoir, and CIPhotoEffectTonal for a more natural grayscale conversions.
Topic: Media Technologies SubTopic: General Tags:
Jan ’23
Reply to [CIRAWFilterImpl semanticSegmentationHairMatte]: unrecognized selector sent to instance
This seems like a bug in the CIRAWFilter implementation. It would be great if you could file a bug report in the Feedback app for that. Thanks! A conceptual note: The CIRAWFilter is meant to be initialized with RAW image data. You are passing it PNG data, which is not what it was designed for. It's a bit surprising that it even works with non-RAW images. If you want to read the auxiliary data embedded in an image, you can instead do the following: let hairMatte = CIImage(contentsOf: imageFileURL, options: [CIImageOption.auxiliarySemanticSegmentationHairMatte: true]) This should work with most CIImage initializers that provide the options parameter. Though I'm not sure if it would work if you load the image with UIImage(named:) as it might strip the auxiliary data on load. Check out CIImageOption for available aux data to load.
Topic: Media Technologies SubTopic: General Tags:
Jan ’23
Reply to CIColorCube sometimes producing no or broken output in macOS 13
It turns out the problem was caused by how we loaded the cube data. Previously, we did it like this: let cubeImage: CGImage = ... // render cube image into a 32-bit float context, since that's the data format needed by CIColorCube let pixelData = UnsafeMutablePointer<simd_float4>.allocate(capacity: cubeImage.width * cubeImage.height) let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue | CGBitmapInfo.byteOrder32Little.rawValue let colorSpace = cubeImage.colorSpace ?? CGColorSpace.sRGBColorSpace guard let bitmapContext = CGContext(data: pixelData, width: cubeImage.width, height: cubeImage.height, bitsPerComponent: MemoryLayout<simd_float4.Scalar>.size * 8, bytesPerRow: MemoryLayout<simd_float4>.size * cubeImage.width, space: colorSpace, bitmapInfo: bitmapInfo) else { assertionFailure("Failed to create bitmap context for conversion") } bitmapContext.draw(cubeImage, in: CGRect(x: 0, y: 0, width: cubeImage.width, height: cubeImage.height)) let data = Data(bytesNoCopy: pixelData, count: bitmapContext.bytesPerRow * bitmapContext.height, deallocator: .free) // pass data to filter Note that we pre-allocated the pixelData buffer and gave it to the CGContext to render the cube image into it. It seems that data was corrupted or released too early in some cases, causing the erroneous behavior described above, even though we assumed that Data(bytesNoCopy:...) would take ownership of the data. To fix this, we let CGContext create its own buffer and copy the cube data after the draw: let cubeImage: CGImage = ... // render cube image into a 32-bit float context, since that's the data format needed by CIColorCube let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue | CGBitmapInfo.byteOrder32Little.rawValue let colorSpace = cubeImage.colorSpace ?? CGColorSpace.sRGBColorSpace guard let bitmapContext = CGContext(data: nil, width: cubeImage.width, height: cubeImage.height, bitsPerComponent: MemoryLayout<simd_float4.Scalar>.size * 8, bytesPerRow: MemoryLayout<simd_float4>.size * cubeImage.width, space: colorSpace, bitmapInfo: bitmapInfo) else { assertionFailure("Failed to create bitmap context for conversion") } bitmapContext.draw(cubeImage, in: CGRect(x: 0, y: 0, width: cubeImage.width, height: cubeImage.height)) guard let pixelData = bitmapContext.data else { assertionFailure("Failed to get cube data") } let data = Data(bytes: pixelData, count: bitmapContext.bytesPerRow * bitmapContext.height) // pass data to filter
Topic: App & System Services SubTopic: Core OS Tags:
Oct ’22
Reply to Adding HDR Gain Map to HEIF/JPEG manually
I think I found the reason why the gain map isn't showing any effect: There seem to be two private {MakerApple} EXIF tags controlling the HDR effect. The one @anteo83 mentioned (0x21) seems to control the global boost to brightness. I guess via some kind of gamma curve tone mapping. But there is also the 0x30 which controls how much the embedded gain map adds to the effect. It seems to range between 0.0 and 8.0, with 0.0 having the biggest effect. When 0x30 is present, 0x21 doesn't need to be present to show the effects of the gain map. They also seem to work in tandem, with 0x21 adding to the global value and 0x30 controlling local values via gain map. There is some great fun to be had with this. Check out the attached image on your XDR or OLED device in Photos. 😁 Edit: I was trying to attach an example image here, but Forums seem to convert the image, losing the gain map in the process. You can find it here.
Topic: Programming Languages SubTopic: Swift Tags:
Sep ’22
Reply to Validation failed error code when attempting to activate a Core Media I/O extension
I encountered the same. When I re-watched the session, I heard Brad say the following: And I need to ensure here that my extension's app group is prefixed by the MachServiceName in order for it to pass validation. I changed the CMIOExtensionMachServiceName in the extension's Info.plist to the same value as my app's and extension's app group name and suddenly it passed validation. I'm not sure if this is the intended configuration. Maybe Apple's Extensions team can clarify what the "prefixed" means here and what the values should look like.
Topic: App & System Services SubTopic: Drivers Tags:
Jun ’22
Reply to EDR doesn't work on iOS?
To enable EDR rendering, all we do is to set the colorPixelFormat to MTLPixelFormatRGBA16Float (note: RGBA, not BGRA) and wantsExtendedDynamicRangeContent to YES. We don't change the colorSpace since it is already set to extended linear sRGB when setting the other properties. As soon as we render pixel values outside [0...1], the screen switches to EDR mode and the potential and current HDR headroom adjust accordingly.
Topic: Graphics & Games SubTopic: Metal Tags:
Replies
Boosts
Views
Activity
Mar ’23
Reply to PHPicker fails to load RAW images
Also filed as FB12042331. Thanks for looking into this!
Replies
Boosts
Views
Activity
Mar ’23
Reply to CIFilter documentation for CIMaximumComponent?
For Core Image documentation in general, I can recommend cifilter.io, though it does not list the newest filters. You can also check out the Filter Magic app, which lets you play with most CIFilters and has a lot of documentation. As for CIMaximumComponent and CIMinimumComponent: They will take the max/min values of R, G, and B and return a pixel with all channels set to this value. Some examples: RGB(1.0, 0.0, 0.0) -> max: RGB(1.0, 1.0, 1.0) | min: RGB(0.0, 0.0, 0.0) RGB(0.5, 0.7, 0.3) -> max: RGB(0.7, 0.7, 0.7) | min: RGB(0.3, 0.3, 0.3) So yes, they turn the image into grayscale, but I might not be what you want since the value doesn't represent perceived lightness of the color. You might want to check out CIPhotoEffectMono, CIPhotoEffectNoir, and CIPhotoEffectTonal for a more natural grayscale conversions.
Topic: Media Technologies SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’23
Reply to [CIRAWFilterImpl semanticSegmentationHairMatte]: unrecognized selector sent to instance
This seems like a bug in the CIRAWFilter implementation. It would be great if you could file a bug report in the Feedback app for that. Thanks! A conceptual note: The CIRAWFilter is meant to be initialized with RAW image data. You are passing it PNG data, which is not what it was designed for. It's a bit surprising that it even works with non-RAW images. If you want to read the auxiliary data embedded in an image, you can instead do the following: let hairMatte = CIImage(contentsOf: imageFileURL, options: [CIImageOption.auxiliarySemanticSegmentationHairMatte: true]) This should work with most CIImage initializers that provide the options parameter. Though I'm not sure if it would work if you load the image with UIImage(named:) as it might strip the auxiliary data on load. Check out CIImageOption for available aux data to load.
Topic: Media Technologies SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’23
Reply to Observe currentEDRHeadroom for changes
Also filed as FB11965555. Thanks!
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
Boosts
Views
Activity
Jan ’23
Reply to `NSValue` containing an `NSAffineTransform` from Swift
I'm not sure what the best way to encode an NSAffineTransform into an NSValue is (maybe NSValue(nonretainedObject:)?), but there is actually a much simpler way for applying a transformation to an image using a CGAffineTransform: let transformedImage = image.transformed(by: CGAffineTransform(...)) No need to use the CIAffineTransform filter.
Topic: Media Technologies SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’23
Reply to Large memory consumption when running Core ML model on A13 GPU
Also reported as FB11871301. Thanks!
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Dec ’22
Reply to CIColorCube sometimes producing no or broken output in macOS 13
It turns out the problem was caused by how we loaded the cube data. Previously, we did it like this: let cubeImage: CGImage = ... // render cube image into a 32-bit float context, since that's the data format needed by CIColorCube let pixelData = UnsafeMutablePointer<simd_float4>.allocate(capacity: cubeImage.width * cubeImage.height) let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue | CGBitmapInfo.byteOrder32Little.rawValue let colorSpace = cubeImage.colorSpace ?? CGColorSpace.sRGBColorSpace guard let bitmapContext = CGContext(data: pixelData, width: cubeImage.width, height: cubeImage.height, bitsPerComponent: MemoryLayout<simd_float4.Scalar>.size * 8, bytesPerRow: MemoryLayout<simd_float4>.size * cubeImage.width, space: colorSpace, bitmapInfo: bitmapInfo) else { assertionFailure("Failed to create bitmap context for conversion") } bitmapContext.draw(cubeImage, in: CGRect(x: 0, y: 0, width: cubeImage.width, height: cubeImage.height)) let data = Data(bytesNoCopy: pixelData, count: bitmapContext.bytesPerRow * bitmapContext.height, deallocator: .free) // pass data to filter Note that we pre-allocated the pixelData buffer and gave it to the CGContext to render the cube image into it. It seems that data was corrupted or released too early in some cases, causing the erroneous behavior described above, even though we assumed that Data(bytesNoCopy:...) would take ownership of the data. To fix this, we let CGContext create its own buffer and copy the cube data after the draw: let cubeImage: CGImage = ... // render cube image into a 32-bit float context, since that's the data format needed by CIColorCube let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue | CGBitmapInfo.byteOrder32Little.rawValue let colorSpace = cubeImage.colorSpace ?? CGColorSpace.sRGBColorSpace guard let bitmapContext = CGContext(data: nil, width: cubeImage.width, height: cubeImage.height, bitsPerComponent: MemoryLayout<simd_float4.Scalar>.size * 8, bytesPerRow: MemoryLayout<simd_float4>.size * cubeImage.width, space: colorSpace, bitmapInfo: bitmapInfo) else { assertionFailure("Failed to create bitmap context for conversion") } bitmapContext.draw(cubeImage, in: CGRect(x: 0, y: 0, width: cubeImage.width, height: cubeImage.height)) guard let pixelData = bitmapContext.data else { assertionFailure("Failed to get cube data") } let data = Data(bytes: pixelData, count: bitmapContext.bytesPerRow * bitmapContext.height) // pass data to filter
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Oct ’22
Reply to CIColorCube sometimes producing no or broken output in macOS 13
This was also filed as FB11736373, together with a demo project.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Oct ’22
Reply to Alternative to `writeHEIF10Representation` for writing a `CIImage` to HEIF10
Are you using the same CIContext in all threads? And can you please post a screenshot of the stack when the crash happens?
Topic: Media Technologies SubTopic: General Tags:
Replies
Boosts
Views
Activity
Oct ’22
Reply to EDR support for images
I also filed Feedback requests for the Image I/O (FB11512515) and Core Image (FB11512528) APIs, as well as PHPicker improvements (FB9706029).
Topic: Media Technologies SubTopic: General Tags:
Replies
Boosts
Views
Activity
Sep ’22
Reply to Adding HDR Gain Map to HEIF/JPEG manually
I think I found the reason why the gain map isn't showing any effect: There seem to be two private {MakerApple} EXIF tags controlling the HDR effect. The one @anteo83 mentioned (0x21) seems to control the global boost to brightness. I guess via some kind of gamma curve tone mapping. But there is also the 0x30 which controls how much the embedded gain map adds to the effect. It seems to range between 0.0 and 8.0, with 0.0 having the biggest effect. When 0x30 is present, 0x21 doesn't need to be present to show the effects of the gain map. They also seem to work in tandem, with 0x21 adding to the global value and 0x30 controlling local values via gain map. There is some great fun to be had with this. Check out the attached image on your XDR or OLED device in Photos. 😁 Edit: I was trying to attach an example image here, but Forums seem to convert the image, losing the gain map in the process. You can find it here.
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Sep ’22
Reply to Is the CIFilterCam source code available anywhere?
Yes, that would be very helpful! I also filed that request under FB10224187.
Replies
Boosts
Views
Activity
Jun ’22
Reply to Validation failed error code when attempting to activate a Core Media I/O extension
I encountered the same. When I re-watched the session, I heard Brad say the following: And I need to ensure here that my extension's app group is prefixed by the MachServiceName in order for it to pass validation. I changed the CMIOExtensionMachServiceName in the extension's Info.plist to the same value as my app's and extension's app group name and suddenly it passed validation. I'm not sure if this is the intended configuration. Maybe Apple's Extensions team can clarify what the "prefixed" means here and what the values should look like.
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Jun ’22
Reply to Allow 16-bit RGBA image formats as input/output of MLModels
Also filed under FB10151072.
Topic: Media Technologies SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jun ’22