STEVEN REPORT
2016-03-12
今更なEICAR
EICARテストファイル
0100 58 POP AX ; AX=0
0101 354F21 XOR AX,214F ; AX=214F
0104 50 PUSH AX ; 214F
0105 254041 AND AX,4140 ; AX=0140
0108 50 PUSH AX ; 0140 214F
0109 5B POP BX ; BX=0140, 214F
010A 345C XOR AL,5C ; AX=011C
010C 50 PUSH AX ; 011C 214F
010D 5A POP DX ; DX=011C, 214F
010E 58 POP AX ; AX=214F
010F 353428 XOR AX,2834 ; AX=097B
0112 50 PUSH AX ; 097B
0113 5E POP SI ; SI=097B
0114 2937 SUB [BX],SI ; [0140]=2B48-097B=21CD
0116 43 INC BX ; BX=0141
0117 43 INC BX ; BX=0142
0118 2937 SUB [BX],SI ; [0142]=2A48-097B=20CD
011A 7D24 JGE 0140 ; JMP 0140: CD 21 CD 20, AH=09 DX=011C INT21, INT 20
011C 45 INC BP ; '$'(24H) terminated output text.
011D 49 DEC CX
011E 43 INC BX
011F 41 INC CX
0120 52 PUSH DX
0121 2D5354 SUB AX,5453
0124 41 INC CX
0125 4E DEC SI
0126 44 INC SP
0127 41 INC CX
0128 52 PUSH DX
0129 44 INC SP
012A 2D414E SUB AX,4E41
012D 54 PUSH SP
012E 49 DEC CX
012F 56 PUSH SI
0130 49 DEC CX
0131 52 PUSH DX
0132 55 PUSH BP
0133 53 PUSH BX
0134 2D5445 SUB AX,4554
0137 53 PUSH BX
0138 54 PUSH SP
0139 2D4649 SUB AX,4946
013C 4C DEC SP
013D 45 INC BP
013E 2124 AND [SI],SP
0140 48 DEC AX
0141 2B482A SUB CX,[BX+SI+2A]
2014-06-24
OpenCLでlanczos3を使った画像の拡大処理(4)
呼出側を書いておく。
実行する場合は、カレントディレクトリに前回の lanczos3.cl を置いておく。
カーネルの文字コードには注意した方が良いかもしれない。
//////// begin: main.cpp
#include <Windows.h>
#include <comdef.h>
#include <wincodec.h>
#include <Shlwapi.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <CL/cl.hpp>
#pragma comment(lib, "Windowscodecs")
#pragma comment(lib, "Shlwapi")
#pragma comment(lib, "OpenCL")
using namespace std;
using namespace cl;
HRESULT read(void **ppData, UINT *pWidth, UINT *pHeight, LPCWSTR pName);
HRESULT writeBmp(LPCWSTR pName, void *pData, UINT width, UINT height);
int main()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED| COINIT_DISABLE_OLE1DDE);
vector<Platform> platforms;
Platform::get(&platforms);
cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0};
Context context(CL_DEVICE_TYPE_ALL, cps);
vector<Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
CommandQueue queue = CommandQueue(context, devices[0], 0);
cout << platforms.size() << " platforms" << endl;
cout << devices.size() << " devices" << endl;
ifstream sourceFileName("lanczos3.cl", ios::in);
string sourceFile(istreambuf_iterator<char>(sourceFileName), (istreambuf_iterator<char>()));
Program::Sources source(1, make_pair(sourceFile.c_str(), sourceFile.length()+1));
Program program(context, source);
program.build(devices);
cl::Kernel kernel_setupTable(program, "lanczos3_setupTable");
cl::Kernel kernel_passFirst(program, "lanczos3_passFirst");
cl::Kernel kernel_passSecond(program, "lanczos3_passSecond");
const cl_int widthDst = 1920;
const cl_int heightDst = 1080;
cl_int elementsHor;
cl_int elementsVert;
void *pData;
UINT width, height;
read(&pData, &width, &height, L"src.bmp");
if (widthDst < width)
elementsHor = 6 * width / widthDst;
else
elementsHor = 6;
if (heightDst < height)
elementsVert = 6 * height / heightDst;
else
elementsVert = 6;
Buffer *pBufferTable = new Buffer(context, CL_MEM_READ_WRITE, (sizeof (float))*((elementsHor*widthDst)+(elementsVert*heightDst)));
ImageFormat format(CL_BGRA, CL_UNORM_INT8);
Image2D *pImgSrc = new Image2D(context, CL_MEM_READ_ONLY, format, width, height);
Image2D *pImgDst = new Image2D(context, CL_MEM_WRITE_ONLY, format, widthDst, heightDst);
Image2D *pImgTmp = new Image2D(context, CL_MEM_READ_WRITE, format, widthDst, height);
//src
cl::size_t<3> origin, region;
region[0] = width;
region[1] = height;
region[2] = 1;
queue.enqueueWriteImage(*pImgSrc, CL_TRUE, origin, region, 0, 0, pData);
//table
kernel_setupTable.setArg(0, *pBufferTable);
kernel_setupTable.setArg(1, cl_int(width));
kernel_setupTable.setArg(2, cl_int(height));
kernel_setupTable.setArg(3, cl_int(widthDst));
kernel_setupTable.setArg(4, cl_int(heightDst));
NDRange wsGlobalTable(widthDst+heightDst);
queue.enqueueNDRangeKernel(kernel_setupTable, NullRange, wsGlobalTable);
queue.finish();
//pass 1
kernel_passFirst.setArg(0, *pImgTmp);
kernel_passFirst.setArg(1, *pImgSrc);
kernel_passFirst.setArg(2, *pBufferTable);
NDRange wsGlobal(widthDst, height);
queue.enqueueNDRangeKernel(kernel_passFirst, NullRange, wsGlobal);
queue.finish();
//pass 2
kernel_passSecond.setArg(0, *pImgDst);
kernel_passSecond.setArg(1, *pImgTmp);
kernel_passSecond.setArg(2, *pBufferTable);
kernel_passSecond.setArg(3, width);
NDRange wsGlobal2(widthDst, heightDst);
queue.enqueueNDRangeKernel(kernel_passSecond, NullRange, wsGlobal2);
queue.finish();
BYTE *pData2 = new BYTE[4*widthDst*heightDst];
region[0] = widthDst;
region[1] = heightDst;
queue.enqueueReadImage(*pImgDst, CL_TRUE, origin, region, 0, 0, pData2);
writeBmp(L"dst.bmp", pData2, widthDst, heightDst);
CoUninitialize();
return 0;
}
_COM_SMARTPTR_TYPEDEF(IWICImagingFactory, __uuidof(IWICImagingFactory));
_COM_SMARTPTR_TYPEDEF(IWICBitmapDecoder, __uuidof(IWICBitmapDecoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameDecode, __uuidof(IWICBitmapFrameDecode));
_COM_SMARTPTR_TYPEDEF(IWICBitmapSource, __uuidof(IWICBitmapSource));
_COM_SMARTPTR_TYPEDEF(IStream, __uuidof(IStream));
_COM_SMARTPTR_TYPEDEF(IWICBitmapEncoder, __uuidof(IWICBitmapEncoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameEncode, __uuidof(IWICBitmapFrameEncode));
HRESULT read(void **ppData, UINT *pWidth, UINT *pHeight, LPCWSTR pName)
{
IWICImagingFactoryPtr pFactory;
IWICBitmapDecoderPtr pDecoder;
IWICBitmapFrameDecodePtr pFrame;
IWICBitmapSourcePtr pBitmap;
UINT stride;
UINT size;
pFactory.CreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER);
pFactory->CreateDecoderFromFilename(pName, NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);
pDecoder->GetFrame(0, &pFrame);
WICConvertBitmapSource(GUID_WICPixelFormat32bppBGR, pFrame, &pBitmap); //DIB section compatible
pBitmap->GetSize(pWidth, pHeight);
stride = 4 * *pWidth;
size = stride * *pHeight;
*ppData = new BYTE[size];
pBitmap->CopyPixels(NULL, stride, size, (BYTE *)*ppData);
return S_OK;
}
HRESULT writeBmp(LPCWSTR pName, void *pData, UINT width, UINT height)
{
IWICImagingFactoryPtr pFactory;
IWICBitmapEncoderPtr pEncoder;
IStreamPtr pStream;
IWICBitmapFrameEncodePtr pFrame;
WICPixelFormatGUID guidFormat;
pFactory.CreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER);
pFactory->CreateEncoder(GUID_ContainerFormatBmp, NULL, &pEncoder);
SHCreateStreamOnFileEx(pName, STGM_READWRITE| STGM_CREATE, FILE_ATTRIBUTE_NORMAL, TRUE, NULL, &pStream);
pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
pEncoder->CreateNewFrame(&pFrame, NULL);
pFrame->Initialize(NULL);
guidFormat = GUID_WICPixelFormat32bppBGR;
pFrame->SetPixelFormat(&guidFormat);
pFrame->SetSize(width, height);
pFrame->WritePixels(height, 4*width, 4*width*height, (BYTE *)pData);
pFrame->Commit();
pEncoder->Commit();
return S_OK;
}
//////// end: main.cpp
実行する場合は、カレントディレクトリに前回の lanczos3.cl を置いておく。
カーネルの文字コードには注意した方が良いかもしれない。
//////// begin: main.cpp
#include <Windows.h>
#include <comdef.h>
#include <wincodec.h>
#include <Shlwapi.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <CL/cl.hpp>
#pragma comment(lib, "Windowscodecs")
#pragma comment(lib, "Shlwapi")
#pragma comment(lib, "OpenCL")
using namespace std;
using namespace cl;
HRESULT read(void **ppData, UINT *pWidth, UINT *pHeight, LPCWSTR pName);
HRESULT writeBmp(LPCWSTR pName, void *pData, UINT width, UINT height);
int main()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED| COINIT_DISABLE_OLE1DDE);
vector<Platform> platforms;
Platform::get(&platforms);
cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0};
Context context(CL_DEVICE_TYPE_ALL, cps);
vector<Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
CommandQueue queue = CommandQueue(context, devices[0], 0);
cout << platforms.size() << " platforms" << endl;
cout << devices.size() << " devices" << endl;
ifstream sourceFileName("lanczos3.cl", ios::in);
string sourceFile(istreambuf_iterator<char>(sourceFileName), (istreambuf_iterator<char>()));
Program::Sources source(1, make_pair(sourceFile.c_str(), sourceFile.length()+1));
Program program(context, source);
program.build(devices);
cl::Kernel kernel_setupTable(program, "lanczos3_setupTable");
cl::Kernel kernel_passFirst(program, "lanczos3_passFirst");
cl::Kernel kernel_passSecond(program, "lanczos3_passSecond");
const cl_int widthDst = 1920;
const cl_int heightDst = 1080;
cl_int elementsHor;
cl_int elementsVert;
void *pData;
UINT width, height;
read(&pData, &width, &height, L"src.bmp");
if (widthDst < width)
elementsHor = 6 * width / widthDst;
else
elementsHor = 6;
if (heightDst < height)
elementsVert = 6 * height / heightDst;
else
elementsVert = 6;
Buffer *pBufferTable = new Buffer(context, CL_MEM_READ_WRITE, (sizeof (float))*((elementsHor*widthDst)+(elementsVert*heightDst)));
ImageFormat format(CL_BGRA, CL_UNORM_INT8);
Image2D *pImgSrc = new Image2D(context, CL_MEM_READ_ONLY, format, width, height);
Image2D *pImgDst = new Image2D(context, CL_MEM_WRITE_ONLY, format, widthDst, heightDst);
Image2D *pImgTmp = new Image2D(context, CL_MEM_READ_WRITE, format, widthDst, height);
//src
cl::size_t<3> origin, region;
region[0] = width;
region[1] = height;
region[2] = 1;
queue.enqueueWriteImage(*pImgSrc, CL_TRUE, origin, region, 0, 0, pData);
//table
kernel_setupTable.setArg(0, *pBufferTable);
kernel_setupTable.setArg(1, cl_int(width));
kernel_setupTable.setArg(2, cl_int(height));
kernel_setupTable.setArg(3, cl_int(widthDst));
kernel_setupTable.setArg(4, cl_int(heightDst));
NDRange wsGlobalTable(widthDst+heightDst);
queue.enqueueNDRangeKernel(kernel_setupTable, NullRange, wsGlobalTable);
queue.finish();
//pass 1
kernel_passFirst.setArg(0, *pImgTmp);
kernel_passFirst.setArg(1, *pImgSrc);
kernel_passFirst.setArg(2, *pBufferTable);
NDRange wsGlobal(widthDst, height);
queue.enqueueNDRangeKernel(kernel_passFirst, NullRange, wsGlobal);
queue.finish();
//pass 2
kernel_passSecond.setArg(0, *pImgDst);
kernel_passSecond.setArg(1, *pImgTmp);
kernel_passSecond.setArg(2, *pBufferTable);
kernel_passSecond.setArg(3, width);
NDRange wsGlobal2(widthDst, heightDst);
queue.enqueueNDRangeKernel(kernel_passSecond, NullRange, wsGlobal2);
queue.finish();
BYTE *pData2 = new BYTE[4*widthDst*heightDst];
region[0] = widthDst;
region[1] = heightDst;
queue.enqueueReadImage(*pImgDst, CL_TRUE, origin, region, 0, 0, pData2);
writeBmp(L"dst.bmp", pData2, widthDst, heightDst);
CoUninitialize();
return 0;
}
_COM_SMARTPTR_TYPEDEF(IWICImagingFactory, __uuidof(IWICImagingFactory));
_COM_SMARTPTR_TYPEDEF(IWICBitmapDecoder, __uuidof(IWICBitmapDecoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameDecode, __uuidof(IWICBitmapFrameDecode));
_COM_SMARTPTR_TYPEDEF(IWICBitmapSource, __uuidof(IWICBitmapSource));
_COM_SMARTPTR_TYPEDEF(IStream, __uuidof(IStream));
_COM_SMARTPTR_TYPEDEF(IWICBitmapEncoder, __uuidof(IWICBitmapEncoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameEncode, __uuidof(IWICBitmapFrameEncode));
HRESULT read(void **ppData, UINT *pWidth, UINT *pHeight, LPCWSTR pName)
{
IWICImagingFactoryPtr pFactory;
IWICBitmapDecoderPtr pDecoder;
IWICBitmapFrameDecodePtr pFrame;
IWICBitmapSourcePtr pBitmap;
UINT stride;
UINT size;
pFactory.CreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER);
pFactory->CreateDecoderFromFilename(pName, NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);
pDecoder->GetFrame(0, &pFrame);
WICConvertBitmapSource(GUID_WICPixelFormat32bppBGR, pFrame, &pBitmap); //DIB section compatible
pBitmap->GetSize(pWidth, pHeight);
stride = 4 * *pWidth;
size = stride * *pHeight;
*ppData = new BYTE[size];
pBitmap->CopyPixels(NULL, stride, size, (BYTE *)*ppData);
return S_OK;
}
HRESULT writeBmp(LPCWSTR pName, void *pData, UINT width, UINT height)
{
IWICImagingFactoryPtr pFactory;
IWICBitmapEncoderPtr pEncoder;
IStreamPtr pStream;
IWICBitmapFrameEncodePtr pFrame;
WICPixelFormatGUID guidFormat;
pFactory.CreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER);
pFactory->CreateEncoder(GUID_ContainerFormatBmp, NULL, &pEncoder);
SHCreateStreamOnFileEx(pName, STGM_READWRITE| STGM_CREATE, FILE_ATTRIBUTE_NORMAL, TRUE, NULL, &pStream);
pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
pEncoder->CreateNewFrame(&pFrame, NULL);
pFrame->Initialize(NULL);
guidFormat = GUID_WICPixelFormat32bppBGR;
pFrame->SetPixelFormat(&guidFormat);
pFrame->SetSize(width, height);
pFrame->WritePixels(height, 4*width, 4*width*height, (BYTE *)pData);
pFrame->Commit();
pEncoder->Commit();
return S_OK;
}
//////// end: main.cpp
2014-06-19
OpenCLでlanczos3を使った画像の拡大処理(3)
重みのテーブルを作成する。
構造は、
float [dstの幅][水平方向タップ数]
float [dstの高さ][垂直方向タップ数]
を隙間なく詰めた感じだ。
//////// begin: lanczos3.cl
float lanczos3(float distance)
{
float weight;
if (FLT_EPSILON > fabs(distance))
weight = 1.0f;
else if (3.0f > fabs(distance))
weight = 3.0f * sin(M_PI_F*distance) * sin(M_PI_F*distance/3.0f) / (M_PI_F*M_PI_F*distance*distance);
else
weight = 0.0f;
return weight;
}
void setupTablePartial(__global float *pWeight, int index, int lengthSrc, int lengthDst, int elements)
{
float center = (index+0.5f) * lengthSrc / lengthDst;
float beginSrc = trunc(center-elements/2);
float distance;
float sumWeight = 0.0f;
//拡大もしくは等倍の場合
if (lengthDst >= lengthSrc)
{
distance = (beginSrc+0.5f)-center;
for (int i=0; i<elements; i++)
{
sumWeight += pWeight[i] = lanczos3(distance);
distance += 1.0f;
}
}
//縮小の場合
else
{
float positionSrc = beginSrc + 0.5f;
for (int i=0; i<elements; i++)
{
distance = (index+0.5f) - (positionSrc * lengthDst / lengthSrc);
sumWeight += pWeight[i] = lanczos3(distance);
positionSrc += 1.0f;
}
}
//ここで割っておくことで、各ドットの計算時に割る手間をなくす
for (int i=0; i<elements; i++)
pWeight[i] /= sumWeight;
}
//テーブルの要素数: elementsHor*widthDst + elementsVert*heightDst;
//elementsHor: 拡大もしくは等倍の場合 6, 縮小の場合 6 * widthSrc / widthDst;
//elementsVert: 拡大もしくは等倍の場合 6, 縮小の場合 6 * heightSrc / heightDst;
__kernel void lanczos3_setupTable(__global float *pTable, int widthSrc, int heightSrc, int widthDst, int heightDst)
{
int index = get_global_id(0);
int elementsHor;
int elementsVert;
if (widthDst+heightDst <= index)
return;
if (widthDst < widthSrc)
elementsHor = 6 * widthSrc / widthDst;
else
elementsHor = 6;
if (heightDst < heightSrc)
elementsVert = 6 * heightSrc / heightDst;
else
elementsVert = 6;
//水平方向の場合
if (widthDst > index)
setupTablePartial(pTable+(elementsHor*index), index, widthSrc, widthDst, elementsHor);
//垂直方向の場合
else
setupTablePartial(pTable+(elementsHor*widthDst)+(elementsVert*(index-widthDst)), index-widthDst, heightSrc, heightDst, elementsVert);
}
//////// end: lanczos3.cl
手元の環境では丸めモードがOpenCL(GPU)とC++(CPU)で異なり、OpenCL(GPU)では0方向への丸めで、C++(CPU)では直近値への丸め(同距離なら偶数寄り)だった。
他にもいくつかの相違があり同一の計算結果を得るのは難しいため、テーブルをCPUで作るのは好ましくない。
構造は、
float [dstの幅][水平方向タップ数]
float [dstの高さ][垂直方向タップ数]
を隙間なく詰めた感じだ。
//////// begin: lanczos3.cl
float lanczos3(float distance)
{
float weight;
if (FLT_EPSILON > fabs(distance))
weight = 1.0f;
else if (3.0f > fabs(distance))
weight = 3.0f * sin(M_PI_F*distance) * sin(M_PI_F*distance/3.0f) / (M_PI_F*M_PI_F*distance*distance);
else
weight = 0.0f;
return weight;
}
void setupTablePartial(__global float *pWeight, int index, int lengthSrc, int lengthDst, int elements)
{
float center = (index+0.5f) * lengthSrc / lengthDst;
float beginSrc = trunc(center-elements/2);
float distance;
float sumWeight = 0.0f;
//拡大もしくは等倍の場合
if (lengthDst >= lengthSrc)
{
distance = (beginSrc+0.5f)-center;
for (int i=0; i<elements; i++)
{
sumWeight += pWeight[i] = lanczos3(distance);
distance += 1.0f;
}
}
//縮小の場合
else
{
float positionSrc = beginSrc + 0.5f;
for (int i=0; i<elements; i++)
{
distance = (index+0.5f) - (positionSrc * lengthDst / lengthSrc);
sumWeight += pWeight[i] = lanczos3(distance);
positionSrc += 1.0f;
}
}
//ここで割っておくことで、各ドットの計算時に割る手間をなくす
for (int i=0; i<elements; i++)
pWeight[i] /= sumWeight;
}
//テーブルの要素数: elementsHor*widthDst + elementsVert*heightDst;
//elementsHor: 拡大もしくは等倍の場合 6, 縮小の場合 6 * widthSrc / widthDst;
//elementsVert: 拡大もしくは等倍の場合 6, 縮小の場合 6 * heightSrc / heightDst;
__kernel void lanczos3_setupTable(__global float *pTable, int widthSrc, int heightSrc, int widthDst, int heightDst)
{
int index = get_global_id(0);
int elementsHor;
int elementsVert;
if (widthDst+heightDst <= index)
return;
if (widthDst < widthSrc)
elementsHor = 6 * widthSrc / widthDst;
else
elementsHor = 6;
if (heightDst < heightSrc)
elementsVert = 6 * heightSrc / heightDst;
else
elementsVert = 6;
//水平方向の場合
if (widthDst > index)
setupTablePartial(pTable+(elementsHor*index), index, widthSrc, widthDst, elementsHor);
//垂直方向の場合
else
setupTablePartial(pTable+(elementsHor*widthDst)+(elementsVert*(index-widthDst)), index-widthDst, heightSrc, heightDst, elementsVert);
}
//////// end: lanczos3.cl
手元の環境では丸めモードがOpenCL(GPU)とC++(CPU)で異なり、OpenCL(GPU)では0方向への丸めで、C++(CPU)では直近値への丸め(同距離なら偶数寄り)だった。
他にもいくつかの相違があり同一の計算結果を得るのは難しいため、テーブルをCPUで作るのは好ましくない。
2014-06-18
OpenCLでlanczos3を使った画像の拡大処理(2)
1: imgSrcの画像を水平方向に補完してimgTmpに書込む。
2: imgTmpの画像を垂直方向に補完してimgDstに書込む
imgTmp.width = imgDst.width
imgTmp.height = imgSrc.height
とする。
//////// begin:
#define GAMMA_CORRECT 0
__kernel void lanczos3_hmag_vmag_passFirst(__write_only image2d_t imgTmp, __read_only image2d_t imgSrc)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_MIRRORED_REPEAT| CLK_FILTER_NEAREST;
int2 coordDst = (int2)(get_global_id(0), get_global_id(1));
float widthSrc = get_image_width(imgSrc);
float widthDst = get_image_width(imgTmp);
float heightDst = get_image_height(imgTmp);
if (widthDst<=coordDst.x || heightDst<=coordDst.y)
return;
float center = (coordDst.x+0.5f) * (widthSrc/widthDst);
float beginSrc = trunc(center-3.0f);
float distance = (beginSrc+0.5f)-center;
float2 coordSample = (float2)(beginSrc, coordDst.y);
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
for (int i=0; i<6; i++)
{
float weight = lanczos3(distance);
float4 color = read_imagef(imgSrc, sampler, coordSample);
#if GAMMA_CORRECT
color = pow(color, 2.2);
#endif
sumColor += color * weight;
sumWeight += weight;
coordSample.x += 1.0f;
distance += 1.0f;
}
write_imagef(imgTmp, coordDst, sumColor / sumWeight);
}
__kernel void lanczos3_hmag_vmag_passSecond(__write_only image2d_t imgDst, __read_only image2d_t imgTmp)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_MIRRORED_REPEAT| CLK_FILTER_NEAREST;
int2 coordDst = (int2)(get_global_id(0), get_global_id(1));
float heightSrc = get_image_height(imgTmp);
float heightDst = get_image_height(imgDst);
float widthDst = get_image_width(imgTmp);
if (widthDst<=coordDst.x || heightDst<=coordDst.y)
return;
float center = (coordDst.y+0.5f) * (heightSrc/heightDst);
float beginSrc = trunc(center-3.0f);
float distance = (beginSrc+0.5f)-center;
float2 coordSample = (float2)(coordDst.x, beginSrc);
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
for (int i=0; i<6; i++)
{
float weight = lanczos3(distance);
sumColor += weight * read_imagef(imgTmp, sampler, coordSample);
sumWeight += weight;
coordSample.y += 1.0f;
distance += 1.0f;
}
#if GAMMA_CORRECT
write_imagef(imgDst, coordDst, pow(sumColor / sumWeight, 1.0f/2.2f));
#else
write_imagef(imgDst, coordDst, sumColor / sumWeight);
#endif
}
//////// end:
遅いカーネルでは平均38.7[ms]、今回のカーネルでは平均8.69[ms]といった具合。
処理点数は36+6から6+6への減少なのにスループットは4倍以上になっているが、原因は分からない。
今回のカーネルでは重みを毎回計算しているが、事前にテーブルを作成しておき、そこから引けば速くなりそうだ。
次回はテーブルの作成のみ書く。
2: imgTmpの画像を垂直方向に補完してimgDstに書込む
imgTmp.width = imgDst.width
imgTmp.height = imgSrc.height
とする。
//////// begin:
#define GAMMA_CORRECT 0
__kernel void lanczos3_hmag_vmag_passFirst(__write_only image2d_t imgTmp, __read_only image2d_t imgSrc)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_MIRRORED_REPEAT| CLK_FILTER_NEAREST;
int2 coordDst = (int2)(get_global_id(0), get_global_id(1));
float widthSrc = get_image_width(imgSrc);
float widthDst = get_image_width(imgTmp);
float heightDst = get_image_height(imgTmp);
if (widthDst<=coordDst.x || heightDst<=coordDst.y)
return;
float center = (coordDst.x+0.5f) * (widthSrc/widthDst);
float beginSrc = trunc(center-3.0f);
float distance = (beginSrc+0.5f)-center;
float2 coordSample = (float2)(beginSrc, coordDst.y);
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
for (int i=0; i<6; i++)
{
float weight = lanczos3(distance);
float4 color = read_imagef(imgSrc, sampler, coordSample);
#if GAMMA_CORRECT
color = pow(color, 2.2);
#endif
sumColor += color * weight;
sumWeight += weight;
coordSample.x += 1.0f;
distance += 1.0f;
}
write_imagef(imgTmp, coordDst, sumColor / sumWeight);
}
__kernel void lanczos3_hmag_vmag_passSecond(__write_only image2d_t imgDst, __read_only image2d_t imgTmp)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_MIRRORED_REPEAT| CLK_FILTER_NEAREST;
int2 coordDst = (int2)(get_global_id(0), get_global_id(1));
float heightSrc = get_image_height(imgTmp);
float heightDst = get_image_height(imgDst);
float widthDst = get_image_width(imgTmp);
if (widthDst<=coordDst.x || heightDst<=coordDst.y)
return;
float center = (coordDst.y+0.5f) * (heightSrc/heightDst);
float beginSrc = trunc(center-3.0f);
float distance = (beginSrc+0.5f)-center;
float2 coordSample = (float2)(coordDst.x, beginSrc);
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
for (int i=0; i<6; i++)
{
float weight = lanczos3(distance);
sumColor += weight * read_imagef(imgTmp, sampler, coordSample);
sumWeight += weight;
coordSample.y += 1.0f;
distance += 1.0f;
}
#if GAMMA_CORRECT
write_imagef(imgDst, coordDst, pow(sumColor / sumWeight, 1.0f/2.2f));
#else
write_imagef(imgDst, coordDst, sumColor / sumWeight);
#endif
}
//////// end:
遅いカーネルでは平均38.7[ms]、今回のカーネルでは平均8.69[ms]といった具合。
処理点数は36+6から6+6への減少なのにスループットは4倍以上になっているが、原因は分からない。
今回のカーネルでは重みを毎回計算しているが、事前にテーブルを作成しておき、そこから引けば速くなりそうだ。
次回はテーブルの作成のみ書く。
2014-06-17
OpenCLでlanczos3を使った画像の拡大処理(1)
細かい事を気にしてはいけない。
手順は次の通りである。
1: 水平方向に補完する。
2: 1で補完したデータを垂直方向に補完する。
水平方向のみの補間は、次のようになる。
1: dstの書込み位置がsrcのどの位置に対応するか求め、これをcとする
2: srcにあるcから距離3以内の6点の輝度に対して、lanczos窓を掛けて総和を求め、重みの総和で割り、これをdstの点の輝度とする
距離の考え方は少々ややこしい。
dst座標での書込み位置をsrc座標で表したもの = (dst座標での書込み位置 + 0.5) * (srcの幅 / dstの幅);
srcのサンプリング開始座標 = trunc(書込み位置をsrc座標で表したもの - 3);
最初のサンプリング点の距離 = (srcのサンプリング開始座標 + 0.5) - 書込み位置をsrc座標で表したもの;
...としておこう。
画像端を容易に処理するために、アドレッシングモードを CLK_ADDRESS_MIRRORED_REPEAT にする。
そのため read_imagef の座標指定をfloat2で行うが、環境によってはCPU処理の場合に期待通り読み取れない場合がある。
今回は遅く汚いカーネルを示す。
より正しく計算する場合はガンマ補正も考慮すべきだが、ここでは割愛する。
//////// begin:
float lanczos3(float d)
{
float weight;
if (0.0f == d)
weight = 1.0f;
else if (3.0f > fabs(d))
weight = 3.0f * sin(M_PI_F*d) * sin(M_PI_F*d/3.0f) / (M_PI_F*M_PI_F*d*d);
else
weight = 0.0f;
return weight;
}
__kernel void lanczos3_hmag_vmag_slow_fuck(__write_only image2d_t imgDst, __read_only image2d_t imgSrc)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_MIRRORED_REPEAT| CLK_FILTER_NEAREST;
float2 coordDst = (float2)(get_global_id(0), get_global_id(1));
float2 sizeSrc = (float2)(get_image_width(imgSrc), get_image_height(imgSrc));
float2 sizeDst = (float2)(get_image_width(imgDst), get_image_height(imgDst));
float2 center;
float2 beginSrc;
float2 distance;
float2 coordSrc;
float4 colorVert[6];
if (sizeDst.x<=coordDst.x || sizeDst.y<=coordDst.y)
return;
center = (coordDst+0.5f) * (sizeSrc / sizeDst);
beginSrc = trunc(center - 3.0f);
distance = (beginSrc+0.5f) - center;
//水平方向に補完してcolorVert[]に入れる
//垂直方向ループ
coordSrc.y = beginSrc.y;
for (int i=0; i<6; i++)
{
float distanceTmp = distance.x;
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
coordSrc.x = beginSrc.x;
//水平方向ループ
for (int j=0; j<6; j++)
{
float weight = lanczos3(distanceTmp);
sumColor += weight * read_imagef(imgSrc, sampler, coordSrc);
sumWeight += weight;
coordSrc.x += 1.0f;;
distanceTmp += 1.0f;
}
colorVert[i] = sumColor / sumWeight;
coordSrc.y += 1.0f;
}
//colorVert[]を補完して書込む
{
float distanceTmp = distance.y;
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
for (int i=0; i<6; i++)
{
float weight = lanczos3(distanceTmp);
sumColor += weight * colorVert[i];
sumWeight += weight;
distanceTmp += 1.0f;
}
write_imagef(imgDst, (int2)(coordDst.x, coordDst.y), sumColor / sumWeight);
}
}
//////// end:
Radeon HD 6870 使用時に1280*720の画像を1920*1080に拡大する処理でローカルワークサイズを自動設定とした場合、平均38[ms]と中々に遅い。
ローカルワークサイズを(16, 12)とした場合は平均36[ms]程度であったが、誤差に近い。
次回は作業領域を使い2パスで処理して計算量を減らすカーネルを挙げる。
手順は次の通りである。
1: 水平方向に補完する。
2: 1で補完したデータを垂直方向に補完する。
水平方向のみの補間は、次のようになる。
1: dstの書込み位置がsrcのどの位置に対応するか求め、これをcとする
2: srcにあるcから距離3以内の6点の輝度に対して、lanczos窓を掛けて総和を求め、重みの総和で割り、これをdstの点の輝度とする
距離の考え方は少々ややこしい。
dst座標での書込み位置をsrc座標で表したもの = (dst座標での書込み位置 + 0.5) * (srcの幅 / dstの幅);
srcのサンプリング開始座標 = trunc(書込み位置をsrc座標で表したもの - 3);
最初のサンプリング点の距離 = (srcのサンプリング開始座標 + 0.5) - 書込み位置をsrc座標で表したもの;
...としておこう。
画像端を容易に処理するために、アドレッシングモードを CLK_ADDRESS_MIRRORED_REPEAT にする。
そのため read_imagef の座標指定をfloat2で行うが、環境によってはCPU処理の場合に期待通り読み取れない場合がある。
今回は遅く汚いカーネルを示す。
より正しく計算する場合はガンマ補正も考慮すべきだが、ここでは割愛する。
//////// begin:
float lanczos3(float d)
{
float weight;
if (0.0f == d)
weight = 1.0f;
else if (3.0f > fabs(d))
weight = 3.0f * sin(M_PI_F*d) * sin(M_PI_F*d/3.0f) / (M_PI_F*M_PI_F*d*d);
else
weight = 0.0f;
return weight;
}
__kernel void lanczos3_hmag_vmag_slow_fuck(__write_only image2d_t imgDst, __read_only image2d_t imgSrc)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_MIRRORED_REPEAT| CLK_FILTER_NEAREST;
float2 coordDst = (float2)(get_global_id(0), get_global_id(1));
float2 sizeSrc = (float2)(get_image_width(imgSrc), get_image_height(imgSrc));
float2 sizeDst = (float2)(get_image_width(imgDst), get_image_height(imgDst));
float2 center;
float2 beginSrc;
float2 distance;
float2 coordSrc;
float4 colorVert[6];
if (sizeDst.x<=coordDst.x || sizeDst.y<=coordDst.y)
return;
center = (coordDst+0.5f) * (sizeSrc / sizeDst);
beginSrc = trunc(center - 3.0f);
distance = (beginSrc+0.5f) - center;
//水平方向に補完してcolorVert[]に入れる
//垂直方向ループ
coordSrc.y = beginSrc.y;
for (int i=0; i<6; i++)
{
float distanceTmp = distance.x;
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
coordSrc.x = beginSrc.x;
//水平方向ループ
for (int j=0; j<6; j++)
{
float weight = lanczos3(distanceTmp);
sumColor += weight * read_imagef(imgSrc, sampler, coordSrc);
sumWeight += weight;
coordSrc.x += 1.0f;;
distanceTmp += 1.0f;
}
colorVert[i] = sumColor / sumWeight;
coordSrc.y += 1.0f;
}
//colorVert[]を補完して書込む
{
float distanceTmp = distance.y;
float sumWeight = 0.0f;
float4 sumColor = 0.0f;
for (int i=0; i<6; i++)
{
float weight = lanczos3(distanceTmp);
sumColor += weight * colorVert[i];
sumWeight += weight;
distanceTmp += 1.0f;
}
write_imagef(imgDst, (int2)(coordDst.x, coordDst.y), sumColor / sumWeight);
}
}
//////// end:
Radeon HD 6870 使用時に1280*720の画像を1920*1080に拡大する処理でローカルワークサイズを自動設定とした場合、平均38[ms]と中々に遅い。
ローカルワークサイズを(16, 12)とした場合は平均36[ms]程度であったが、誤差に近い。
次回は作業領域を使い2パスで処理して計算量を減らすカーネルを挙げる。
CodeXL
CodeXL – Powerful Debugging, Profiling & Analysis
http://developer.amd.com/tools-and-sdks/opencl-zone/opencl-tools-sdks/codexl/
カーネルのコンパイルを行うだけでも十分に有用だが、詰めたい場合に GPU: Performance Counter, Application Timeline Trace を見ると良さそうだ。
http://developer.amd.com/tools-and-sdks/opencl-zone/opencl-tools-sdks/codexl/
カーネルのコンパイルを行うだけでも十分に有用だが、詰めたい場合に GPU: Performance Counter, Application Timeline Trace を見ると良さそうだ。
OpenCLのImageObjectsでの画像の扱いについて
細かい事を気にしてはいけない。
ホスト側の関数。
clCreateImage: イメージオブジェクトの作成。
clReleaseMemObject: バッファ或いはイメージオブジェクトの解放。
clEnqueueWriteImage: イメージオブジェクトへの書込み。
clEnqueueReadImage: イメージオブジェクトからの読出し。
cl_channel_order について。
メモリ上でのバイト順を指定する。
CL_RGBA: 下位から順にR, G, B, A。
CL_BGRA: 下位から順にB, G, R, A、これはWindowsのDIBSectionと同じである。
cl_channel_type について。
CL_UNORM_INT8: 符号無し, カーネルでの読書きは0.0~1.0, メモリ上のデータは符号なし8ビット整数、一般的にはこれを扱う。
カーネル側の関数など。
read_imagef: イメージオブジェクトから読出す。座標を整数で指定する場合はサンプラに制限がある。AMD製ソフトウェアでCPUデバイスを使用する場合、実数座標だと不具合が出るようだ。
write_imagef: イメージオブジェクトへ書込む。
仮引数image2d_t は、__write_only か __read_only の一方のみで修飾する必要がある。(デフォで __read_only 修飾だが。)
例として、画像をグレイスケールに変換するプログラムを示す。
画像の読書きについては、前回の投稿を参照すること。
Windowscodecs.lib, Shlwapi.lib, OpenCL.lib をリンクすること。
//////// begin: grayscale.cl
__kernel void grayscale(__write_only image2d_t imgDst, __read_only image2d_t imgSrc)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_NONE| CLK_FILTER_NEAREST;
int2 coord;
float4 color;
coord.x = get_global_id(0);
coord.y = get_global_id(1);
if (get_image_width(imgDst)<=coord.x || get_image_height(imgDst)<=coord.y)
return;
color = read_imagef(imgSrc, sampler, coord);
color.xyz = dot(color.xyz, (float3)(0.299f, 0.587f, 0.114f)); //r=g=b = r*0.299 + g*0.587 + b*0.114
color.w = 0.0f; //a = 0
write_imagef(imgDst, coord, color);
}
//////// end: grayscale.cl
//////// begin: C++
#include <Windows.h>
#include <comdef.h>
#include <wincodec.h>
#include <Shlwapi.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <CL/cl.hpp>
using namespace std;
using namespace cl;
HRESULT read(void **ppData, UINT *pWidth, UINT *pHeight, LPCWSTR pName);
HRESULT writeBmp(LPCWSTR pName, void *pData, UINT width, UINT height);
int main()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED| COINIT_DISABLE_OLE1DDE);
vector<Platform> platforms;
Platform::get(&platforms);
cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0};
Context context(CL_DEVICE_TYPE_ALL, cps);
vector<Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
CommandQueue queue = CommandQueue(context, devices[0], 0);
cout << platforms.size() << " platforms" << endl;
cout << devices.size() << " devices" << endl;
ifstream sourceFileName("grayscale.cl", ios::in);
string sourceFile(istreambuf_iterator<char>(sourceFileName), (istreambuf_iterator<char>()));
Program::Sources source(1, make_pair(sourceFile.c_str(), sourceFile.length()+1));
Program program(context, source);
program.build(devices);
cl::Kernel kernel_grayscale(program, "grayscale");
void *pData;
UINT width, height;
read(&pData, &width, &height, L"src.jpg");
ImageFormat format(CL_BGRA, CL_UNORM_INT8);
Image2D *pImgDst = new Image2D(context, CL_MEM_WRITE_ONLY, format, width, height);
Image2D *pImgSrc = new Image2D(context, CL_MEM_READ_ONLY, format, width, height);
cl::size_t<3> origin, region;
region[0] = width;
region[1] = height;
region[2] = 1;
queue.enqueueWriteImage(*pImgSrc, CL_TRUE, origin, region, 0, 0, pData);
kernel_grayscale.setArg(0, *pImgDst);
kernel_grayscale.setArg(1, *pImgSrc);
NDRange wsGlobal(width, height);
queue.enqueueNDRangeKernel(kernel_grayscale, NullRange, wsGlobal);
queue.enqueueReadImage(*pImgDst, CL_TRUE, origin, region, 0, 0, pData);
writeBmp(L"dst.bmp", pData, width, height);
CoUninitialize();
return 0;
}
//////// end: C++
ホスト側の関数。
clCreateImage: イメージオブジェクトの作成。
clReleaseMemObject: バッファ或いはイメージオブジェクトの解放。
clEnqueueWriteImage: イメージオブジェクトへの書込み。
clEnqueueReadImage: イメージオブジェクトからの読出し。
cl_channel_order について。
メモリ上でのバイト順を指定する。
CL_RGBA: 下位から順にR, G, B, A。
CL_BGRA: 下位から順にB, G, R, A、これはWindowsのDIBSectionと同じである。
cl_channel_type について。
CL_UNORM_INT8: 符号無し, カーネルでの読書きは0.0~1.0, メモリ上のデータは符号なし8ビット整数、一般的にはこれを扱う。
カーネル側の関数など。
read_imagef: イメージオブジェクトから読出す。座標を整数で指定する場合はサンプラに制限がある。AMD製ソフトウェアでCPUデバイスを使用する場合、実数座標だと不具合が出るようだ。
write_imagef: イメージオブジェクトへ書込む。
仮引数image2d_t は、__write_only か __read_only の一方のみで修飾する必要がある。(デフォで __read_only 修飾だが。)
例として、画像をグレイスケールに変換するプログラムを示す。
画像の読書きについては、前回の投稿を参照すること。
Windowscodecs.lib, Shlwapi.lib, OpenCL.lib をリンクすること。
//////// begin: grayscale.cl
__kernel void grayscale(__write_only image2d_t imgDst, __read_only image2d_t imgSrc)
{
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_NONE| CLK_FILTER_NEAREST;
int2 coord;
float4 color;
coord.x = get_global_id(0);
coord.y = get_global_id(1);
if (get_image_width(imgDst)<=coord.x || get_image_height(imgDst)<=coord.y)
return;
color = read_imagef(imgSrc, sampler, coord);
color.xyz = dot(color.xyz, (float3)(0.299f, 0.587f, 0.114f)); //r=g=b = r*0.299 + g*0.587 + b*0.114
color.w = 0.0f; //a = 0
write_imagef(imgDst, coord, color);
}
//////// end: grayscale.cl
//////// begin: C++
#include <Windows.h>
#include <comdef.h>
#include <wincodec.h>
#include <Shlwapi.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <CL/cl.hpp>
using namespace std;
using namespace cl;
HRESULT read(void **ppData, UINT *pWidth, UINT *pHeight, LPCWSTR pName);
HRESULT writeBmp(LPCWSTR pName, void *pData, UINT width, UINT height);
int main()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED| COINIT_DISABLE_OLE1DDE);
vector<Platform> platforms;
Platform::get(&platforms);
cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0};
Context context(CL_DEVICE_TYPE_ALL, cps);
vector<Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
CommandQueue queue = CommandQueue(context, devices[0], 0);
cout << platforms.size() << " platforms" << endl;
cout << devices.size() << " devices" << endl;
ifstream sourceFileName("grayscale.cl", ios::in);
string sourceFile(istreambuf_iterator<char>(sourceFileName), (istreambuf_iterator<char>()));
Program::Sources source(1, make_pair(sourceFile.c_str(), sourceFile.length()+1));
Program program(context, source);
program.build(devices);
cl::Kernel kernel_grayscale(program, "grayscale");
void *pData;
UINT width, height;
read(&pData, &width, &height, L"src.jpg");
ImageFormat format(CL_BGRA, CL_UNORM_INT8);
Image2D *pImgDst = new Image2D(context, CL_MEM_WRITE_ONLY, format, width, height);
Image2D *pImgSrc = new Image2D(context, CL_MEM_READ_ONLY, format, width, height);
cl::size_t<3> origin, region;
region[0] = width;
region[1] = height;
region[2] = 1;
queue.enqueueWriteImage(*pImgSrc, CL_TRUE, origin, region, 0, 0, pData);
kernel_grayscale.setArg(0, *pImgDst);
kernel_grayscale.setArg(1, *pImgSrc);
NDRange wsGlobal(width, height);
queue.enqueueNDRangeKernel(kernel_grayscale, NullRange, wsGlobal);
queue.enqueueReadImage(*pImgDst, CL_TRUE, origin, region, 0, 0, pData);
writeBmp(L"dst.bmp", pData, width, height);
CoUninitialize();
return 0;
}
//////// end: C++
登録:
投稿 (Atom)