BCB Client

- C++ Builderを用いたWindowsプログラミングメモ -

ScanLine vs Pixels

Builderには画像読込について、2つの方法があります。
以前に出てきたScanLineと、今回実験するPixelsです。

Pixelsのほうはピクセルの位置を2次元配列で指定でき
分かり易い形になっていますが、妙に遅いとの評判。

果たして本当に遅いのか...


vader.png


 ←サイズ800x600のbmp画像を元に
  ScanLineとPixesを比べてみます。








実験1では、単純に1ピクセルずつ反転していきます。
実験2では、ランダムに作成したX,Y位置のピクセルを反転していきます。


scanline_vs_pixels.png


 ←こんなフォームを作った。
  TMemoは計測結果表示
  ScanLine, Pixelsボタンはそれぞれ実行させる用。








<Unit1.h>
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE 管理のコンポーネント
	TMemo *Memo1;
	TButton *ScanLineBtn;
	TButton *PixelsBtn;
	void __fastcall ScanLineBtnClick(TObject *Sender);
	void __fastcall PixelsBtnClick(TObject *Sender);
private:	// ユーザー宣言
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif





<Unit1.cpp>
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	Randomize();	// 乱数の初期化
}
//---------------------------------------------------------------------------
// ScanLineで実行
void __fastcall TForm1::ScanLineBtnClick(TObject *Sender)
{
	Graphics::TBitmap *BM = new Graphics::TBitmap;
	BM->LoadFromFile("vader.bmp");

	LARGE_INTEGER fr, scnt, ecnt;
	DWORD tim;

	//-- 実験1 -- 順番に読み込んで反転
	QueryPerformanceFrequency(&fr);
	QueryPerformanceCounter(&scnt);		// 計測開始

	for( int y=0; y<BM->Height; y++ ){
		Byte *Line = (Byte*)BM->ScanLine[y];	// ScanLineで実行
		int i=0;
		for( int x=0; x<BM->Width; x++ ){
			Line[i]   = ~Line[i];		// 反転
			Line[i+1] = ~Line[i+1];
			Line[i+2] = ~Line[i+2];
			i+=3;
		}
	}
	QueryPerformanceCounter(&ecnt);		// 計測終了
	tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);

	Memo1->Lines->Add("01:ScanLine--> " + IntToStr(tim) + " ms");
	BM->SaveToFile("ScanLine_01.bmp");


	//-- 実験2 -- ランダムに読み込んで反転
	QueryPerformanceFrequency(&fr);
	QueryPerformanceCounter(&scnt);		// 計測開始

	for( int n=0; n<BM->Width*BM->Height; n++ ){
		int x = random(BM->Width);	// 乱数でX, Yを決定
		int y = random(BM->Height);
		Byte *Line = (Byte*)BM->ScanLine[y];	// ScanLineで実行
		Line[y*BM->Width*3 + x]   = ~Line[y*BM->Width*3 + x];	// 反転
		Line[y*BM->Width*3 + x+1] = ~Line[y*BM->Width*3 + x+1];
		Line[y*BM->Width*3 + x+2] = ~Line[y*BM->Width*3 + x+2];
	}
	QueryPerformanceCounter(&ecnt);		// 計測終了
	tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);

	Memo1->Lines->Add("02:ScanLine--> " + IntToStr(tim) + " ms");
	BM->SaveToFile("ScanLine_02.bmp");
                  
	delete BM;
}
//---------------------------------------------------------------------------
// Pixelsで実行
void __fastcall TForm1::PixelsBtnClick(TObject *Sender)
{
	Graphics::TBitmap *BM = new Graphics::TBitmap;
	BM->LoadFromFile("vader.bmp");

	LARGE_INTEGER fr, scnt, ecnt;
	DWORD tim;

	//-- 実験1 -- 順番に読み込んで反転
	QueryPerformanceFrequency(&fr);
	QueryPerformanceCounter(&scnt);		// 計測開始

	for( int y=0; y<BM->Height; y++ ){
		for( int x=0; x<BM->Width; x++ ){
			// Pixelsで反転させる
			BM->Canvas->Pixels[x][y] = ~BM->Canvas->Pixels[x][y];
		}
	}
	QueryPerformanceCounter(&ecnt);		// 計測終了
	tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);

	Memo1->Lines->Add("01:Pixels--> " + IntToStr(tim) + " ms");
	BM->SaveToFile("Pixels_01.bmp");


	//-- 実験2 -- ランダムに読み込んで反転
	QueryPerformanceFrequency(&fr);
	QueryPerformanceCounter(&scnt);		// 計測開始

	for( int n=0; n<BM->Width*BM->Height; n++ ){
		int x = random(BM->Width);	// 乱数でX, Yを決定
		int y = random(BM->Height);
		// Pixelsで反転させる
		BM->Canvas->Pixels[x][y] = ~BM->Canvas->Pixels[x][y];
	}
	QueryPerformanceCounter(&ecnt);		// 計測終了
	tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);

	Memo1->Lines->Add("02:Pixels--> " + IntToStr(tim) + " ms");
	BM->SaveToFile("Pixels_02.bmp");

	delete BM;
}
//---------------------------------------------------------------------------

結果:

01:ScanLine--> 5 ms
02:ScanLine--> 255 ms
01:Pixels--> 800 ms
02:Pixels--> 991 ms

というわけで、やっぱりPixelsは遅かった...



画像としては↓こんな感じになります。(左:ScanLine 右:Pixels)
scanline_vs_pixels_01.pngscanline_vs_pixels_02.png










補足:
今回は計測用にQueryPerformanceFrequency、QueryPerformanceCounterを使っています。
このAPIを使うと最大精度でナノ秒まで見る事が出来る高精度なものです。
計測終了時に * 1000 をしている箇所を変えてやればマイクロ秒などに変更できます。
timeGetTimeも計測用に使えるけど、ミリ秒まで。

それと、処理結果を保存する為にSaveToFileも利用しています。
結果画像を確認する際には便利なもので、ちょいちょい使います。
今回はファイルパスを指定していないのでexeファイルがある場所に作成されます。

[ トップ | 一覧 | 前へ | 次へ ]

  • トップ
  • 一覧
  • リンク
  • 掲示板

Copyright © BCB Client. All Rights Reserved. Template by ネットマニア