Counting coloured pixels on the GPU - Theory(基于GPU原理的彩色像素计数)
问题描述
我有一张128 x 128像素的图像。
它被分解为8x8网格。
每个网格挡路包含16x16像素。
要求
我要计算我的图像包含多少黑色像素。
直通方式:
我可以通过逐行、逐列、遍历整个图像并检查像素是否为黑色来完成此操作。
GPU方式
.但我想知道如果使用图形处理器,我可以将图像分解成块/块,计算每个挡路中的所有像素,然后将结果求和。
例如:
如果您看图片的左上角:
第一个挡路,‘A1’(A行第1列)包含一个16x16像素的网格,通过手动计数,我知道有16个黑色像素。
第二个挡路:‘A2’,(A行第2列)包含一个16x16像素的网格,通过手动计数我知道有62个黑色像素。
此示例的所有其他块为空/空。
如果我在程序中运行图像,我应该会得到答案:16+62=78黑像素。
推理
我的理解是,GPU可以并行操作大量数据,有效地对分布在多个GPU线程上的数据块运行一个小程序。 我不担心速度/性能,我只想知道GPU是否能做到这一点?
推荐答案
实际上,通用GPU(例如,A8及以后的苹果设备中的GPU)不仅能够而且能够解决此类并行数据处理问题。
Apple在他们的平台中引入了使用Metal的数据并行处理,通过一些简单的代码,您可以使用GPU解决类似的问题。即使这也可以使用其他框架来完成,我还是包含了Metal+SWIFT案例的一些代码作为概念证明。
以下代码在OS X Sierra上作为SWIFT命令行工具运行,并且是使用Xcode9构建的(是的,我知道它是测试版)。您可以从我的github repo获取完整的项目。ASmain.swift
:
import Foundation
import Metal
import CoreGraphics
import AppKit
guard FileManager.default.fileExists(atPath: "./testImage.png") else {
print("./testImage.png does not exist")
exit(1)
}
let url = URL(fileURLWithPath: "./testImage.png")
let imageData = try Data(contentsOf: url)
guard let image = NSImage(data: imageData),
let imageRef = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
print("Failed to load image data")
exit(1)
}
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * imageRef.width
var rawData = [UInt8](repeating: 0, count: Int(bytesPerRow * imageRef.height))
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue).union(.byteOrder32Big)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGContext(data: &rawData,
width: imageRef.width,
height: imageRef.height,
bitsPerComponent: 8,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo.rawValue)
let fullRect = CGRect(x: 0, y: 0, width: CGFloat(imageRef.width), height: CGFloat(imageRef.height))
context?.draw(imageRef, in: fullRect, byTiling: false)
// Get access to iPhone or iPad GPU
guard let device = MTLCreateSystemDefaultDevice() else {
exit(1)
}
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .rgba8Unorm,
width: Int(imageRef.width),
height: Int(imageRef.height),
mipmapped: true)
let texture = device.makeTexture(descriptor: textureDescriptor)
let region = MTLRegionMake2D(0, 0, Int(imageRef.width), Int(imageRef.height))
texture.replace(region: region, mipmapLevel: 0, withBytes: &rawData, bytesPerRow: Int(bytesPerRow))
// Queue to handle an ordered list of command buffers
let commandQueue = device.makeCommandQueue()
// Buffer for storing encoded commands that are sent to GPU
let commandBuffer = commandQueue.makeCommandBuffer()
// Access to Metal functions that are stored in Shaders.metal file, e.g. sigmoid()
guard let defaultLibrary = device.makeDefaultLibrary() else {
print("Failed to create default metal shader library")
exit(1)
}
// Encoder for GPU commands
let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()
// hardcoded to 16 for now (recommendation: read about threadExecutionWidth)
var threadsPerGroup = MTLSize(width:16, height:16, depth:1)
var numThreadgroups = MTLSizeMake(texture.width / threadsPerGroup.width,
texture.height / threadsPerGroup.height,
1);
// b. set up a compute pipeline with Sigmoid function and add it to encoder
let countBlackProgram = defaultLibrary.makeFunction(name: "countBlack")
let computePipelineState = try device.makeComputePipelineState(function: countBlackProgram!)
computeCommandEncoder.setComputePipelineState(computePipelineState)
// set the input texture for the countBlack() function, e.g. inArray
// atIndex: 0 here corresponds to texture(0) in the countBlack() function
computeCommandEncoder.setTexture(texture, index: 0)
// create the output vector for the countBlack() function, e.g. counter
// atIndex: 1 here corresponds to buffer(0) in the Sigmoid function
var counterBuffer = device.makeBuffer(length: MemoryLayout<UInt32>.size,
options: .storageModeShared)
computeCommandEncoder.setBuffer(counterBuffer, offset: 0, index: 0)
computeCommandEncoder.dispatchThreadgroups(numThreadgroups, threadsPerThreadgroup: threadsPerGroup)
computeCommandEncoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
// a. Get GPU data
// outVectorBuffer.contents() returns UnsafeMutablePointer roughly equivalent to char* in C
var data = NSData(bytesNoCopy: counterBuffer.contents(),
length: MemoryLayout<UInt32>.size,
freeWhenDone: false)
// b. prepare Swift array large enough to receive data from GPU
var finalResultArray = [UInt32](repeating: 0, count: 1)
// c. get data from GPU into Swift array
data.getBytes(&finalResultArray, length: MemoryLayout<UInt>.size)
print("Found (finalResultArray[0]) non-white pixels")
// d. YOU'RE ALL SET!
另外,在Shaders.metal
中:
#include <metal_stdlib>
using namespace metal;
kernel void
countBlack(texture2d<float, access::read> inArray [[texture(0)]],
volatile device uint *counter [[buffer(0)]],
uint2 gid [[thread_position_in_grid]]) {
// Atomic as we need to sync between threadgroups
device atomic_uint *atomicBuffer = (device atomic_uint *)counter;
float3 inColor = inArray.read(gid).rgb;
if(inColor.r != 1.0 || inColor.g != 1.0 || inColor.b != 1.0) {
atomic_fetch_add_explicit(atomicBuffer, 1, memory_order_relaxed);
}
}
我使用这个问题学习了一些关于Metal和数据并行计算的知识,因此大部分代码都被用作在线文章和编辑文章中的样板。请花时间访问下面提到的来源,以获得更多示例。此外,针对此特定问题的代码几乎都是硬编码的,但是您应该不会有太大的问题来修改它。
来源:
http://flexmonkey.blogspot.com.ar/2016/05/histogram-equalisation-with-metal.html
http://metalbyexample.com/introduction-to-compute/
http://memkite.com/blog/2014/12/15/data-parallel-programming-with-metal-and-swift-for-iphoneipad-gpu/
这篇关于基于GPU原理的彩色像素计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:基于GPU原理的彩色像素计数


- 在测试浓缩咖啡时,Android设备不会在屏幕上启动活动 2022-01-01
- Android - 我如何找出用户有多少未读电子邮件? 2022-01-01
- MalformedJsonException:在第1行第1列路径中使用JsonReader.setLenient(True)接受格式错误的JSON 2022-01-01
- 用 Swift 实现 UITextFieldDelegate 2022-01-01
- 想使用ViewPager,无法识别android.support.*? 2022-01-01
- Android - 拆分 Drawable 2022-01-01
- Android viewpager检测滑动超出范围 2022-01-01
- android 4中的android RadioButton问题 2022-01-01
- 使用自定义动画时在 iOS9 上忽略 edgesForExtendedLayout 2022-01-01
- 如何检查发送到 Android 应用程序的 Firebase 消息的传递状态? 2022-01-01