<2022-04-21 周四>
如何写ScaleImage()
的硬件加速函数(二)
搞了一天也没有搞出来kernel
函数怎么写,还得仔细分析一下ScaleImage()
函数流程:
- 从
GraphicsMagick
的ScaleImage()
入手,它比ImageMagick
好懂。 - 大循环的第一个
if-else
分支处理Y
方向,即垂直方向,它用到两个动态数组x_vector
和y_vector
,它们的长度相等,都是原图的宽度,y_scale
小于1
是缩小,y_scale
大于1
是放大,不管是放大还是缩小,都会先读一行原图像素放到x_vector
中。 - 如果是缩小的话,各像素乘以
y_scale
的结果存放到y_vector
中,可能会继续读取下一行原图进行累积计算。 - 如果是放大的话,会将
y_vector+y_span*x_vector
的结果放到一个临时变量pixel
中,之所以要放到pixel
中是因为要处理计算结果大于255.0
的情况,且可能y_vector
在这里首次被使用,所以它申请内存时必须初始始化为0
,所以它用的是MagickAllocateClearedArray()
函数。
y_vector=MagickAllocateClearedArray(DoublePixelPacket *,
image->columns,sizeof(DoublePixelPacket));
pixel
的结果是存到s
中,而s=scanline;
,且scanline=x_vector;
,所以到这里x_vector
存放的是Y
方向的处理结果。- 然后这里到第二个
if-else
分支,即处理X
方向,代码同第一个if-else
分支大同小异,但要注意else
,它有一个稍大的循环。最终结果存在t
即scale_scanline
中。 scale_scanline
是以一个以目标宽度为长度的动态数组。
我尝试写的kernel
函数模仿了ScaleImage()
的很多代码,实际上不能工作,以试着重新理解opencl
的方式,理解work-group
和work-item
,仅有的收获在:
STRINGIFY(
__kernel // __attribute__((reqd_work_group_size(256, 1, 1)))
void ScaleFilter(const __global CLQuantum *inputImage, const unsigned int matte_or_cmyk,
const unsigned int inputColumns, const unsigned int inputRows, __global CLQuantum *filteredImage,
const unsigned int filteredColumns, const unsigned int filteredRows,
const float resizeFilterScale,
__local CLQuantum *inputImageCache, const int numCachedPixels,
const unsigned int pixelPerWorkgroup, const unsigned int pixelChunkSize,
__local float4 *outputPixelCache, __local float *densityCache, __local float *gammaCache)
{
const int x=get_global_id(0);
const int y=get_global_id(1);
const unsigned int columns=get_global_size(0);
int cy=y;
float4 pixel=ReadAllChannels(inputImage,4,columns,x,cy);
pixel/=4.5;
WriteAllChannels(filteredImage,4,filteredColumns,
x*filteredColumns/inputColumns,y*filteredRows/inputRows,pixel);
}
)
似乎生成的图片没有变形,我加了pixel/=4.5;
这行代码是为了调试方便,它的效果是使图片变暗。仅此简单的代码也能完成缩放功能(备注:缩小没问题,放大不行),但是WriteAllChannels()
的x
和y
坐标要从work-item
的视角看work-group
,目前只能以x*filteredColumns/inputColumns
和y*filteredRows/inputRows
来代替,以验证我对work-group
和work-item
的理解,从上面的代码看,我似乎理解了一些。
参照下面的ImageMagick
代码理解:
__kernel void Contrast(__global CLQuantum *image,
const unsigned int number_channels,const int sign)
{
const int x=get_global_id(0);
const int y=get_global_id(1);
const unsigned int columns=get_global_size(0);
float4 pixel=ReadAllChannels(image,number_channels,columns,x,y);
if (number_channels < 3)
pixel.y=pixel.z=pixel.x;
pixel=ConvertRGBToHSB(pixel);
float brightness=pixel.z;
brightness+=0.5f*sign*(0.5f*(sinpi(brightness-0.5f)+1.0f)-brightness);
brightness=clamp(brightness,0.0f,1.0f);
pixel.z=brightness;
pixel=ConvertHSBToRGB(pixel);
WriteAllChannels(image,number_channels,columns,x,y,pixel);
}
)
此外我觉得没有必要学AccelerateResizeImage()
函数去增加filteredImageBuffer
变量,可以学ImageMagick
的AccelerateContrastImage()
函数,在ComputeContrastImage()
中直接调用kernel
函数,这样可以少一层函数调用。
commit
:fake run: AccelerateScaleImage(), still trying, with a little bit of progress。