PDA

View Full Version : الگوريتم canny



asefy2008
دوشنبه 30 فروردین 1389, 09:33 صبح
سلام
كسي از دوستان كد الگوريتم canny رو داره بزاره ممنون ميشم؟(حتي المكان با سي شارپ)

مصطفی ساتکی
دوشنبه 30 فروردین 1389, 10:49 صبح
سلام کدشو به دلفی واست گذاشتم که زمانیکه canny رو از Delphi به C#‎ می بره عملکردشو هم یاد بگیری



unit GBlur2;

interface

uses
Windows, Graphics;

type
PRGBTriple = ^TRGBTriple;
TRGBTriple = packed record
b: byte; {easier to type than rgbtBlue}
g: byte;
r: byte;
end;
PRow = ^TRow;
TRow = array[0..1000000] of TRGBTriple;
PPRows = ^TPRows;
TPRows = array[0..1000000] of PRow;

const
MaxKernelSize = 100;

type
TKernelSize = 1..MaxKernelSize;
TKernel = record
Size: TKernelSize;
Weights: array[-MaxKernelSize..MaxKernelSize] of single;
end;
{the idea is that when using a TKernel you ignore the Weights except
for Weights in the range -Size..Size.}

procedure GBlur(theBitmap: TBitmap; radius: double);

implementation

uses
SysUtils;

procedure MakeGaussianKernel(var K: TKernel; radius: double; MaxData, DataGranularity: double);
{makes K into a gaussian kernel with standard deviation = radius. For the current application
you set MaxData = 255 and DataGranularity = 1. Now the procedure sets the value of K.Size so
that when we use K we will ignore the Weights that are so small they can't possibly matter. (Small
Size is good because the execution time is going to be propertional to K.Size.)}
var
j: integer;
temp, delta: double;
KernelSize: TKernelSize;
begin
for j := Low(K.Weights) to High(K.Weights) do
begin
temp := j / radius;
K.Weights[j] := exp(-temp * temp / 2);
end;
{now divide by constant so sum(Weights) = 1:}
temp := 0;
for j := Low(K.Weights) to High(K.Weights) do
temp := temp + K.Weights[j];
for j := Low(K.Weights) to High(K.Weights) do
K.Weights[j] := K.Weights[j] / temp;
{now discard (or rather mark as ignorable by setting Size) the entries that are too small to matter.
This is important, otherwise a blur with a small radius will take as long as with a large radius...}
KernelSize := MaxKernelSize;
delta := DataGranularity / (2 * MaxData);
temp := 0;
while (temp < delta) and (KernelSize > 1) do
begin
temp := temp + 2 * K.Weights[KernelSize];
dec(KernelSize);
end;
K.Size := KernelSize;
{now just to be correct go back and jiggle again so the sum of the entries we'll be using is exactly 1}
temp := 0;
for j := -K.Size to K.Size do
temp := temp + K.Weights[j];
for j := -K.Size to K.Size do
K.Weights[j] := K.Weights[j] / temp;
end;

function TrimInt(Lower, Upper, theInteger: integer): integer;
begin
if (theInteger <= Upper) and (theInteger >= Lower) then
result := theInteger
else if theInteger > Upper then
result := Upper
else
result := Lower;
end;

function TrimReal(Lower, Upper: integer; x: double): integer;
begin
if (x < upper) and (x >= lower) then
result := trunc(x)
else if x > Upper then
result := Upper
else
result := Lower;
end;

procedure BlurRow(var theRow: array of TRGBTriple; K: TKernel; P: PRow);
var
j, n, LocalRow: integer;
tr, tg, tb: double; {tempRed, etc}
w: double;
begin
for j := 0 to High(theRow) do
begin
tb := 0;
tg := 0;
tr := 0;
for n := -K.Size to K.Size do
begin
w := K.Weights[n];
{the TrimInt keeps us from running off the edge of the row...}
with theRow[TrimInt(0, High(theRow), j - n)] do
begin
tb := tb + w * b;
tg := tg + w * g;
tr := tr + w * r;
end;
end;
with P[j] do
begin
b := TrimReal(0, 255, tb);
g := TrimReal(0, 255, tg);
r := TrimReal(0, 255, tr);
end;
end;
Move(P[0], theRow[0], (High(theRow) + 1) * Sizeof(TRGBTriple));
end;

procedure GBlur(theBitmap: TBitmap; radius: double);
var
Row, Col: integer;
theRows: PPRows;
K: TKernel;
ACol: PRow;
P: PRow;
begin
if (theBitmap.HandleType <> bmDIB) or (theBitmap.PixelFormat <> pf24Bit) then
raise exception.Create('GBlur only works for 24-bit bitmaps');
MakeGaussianKernel(K, radius, 255, 1);
GetMem(theRows, theBitmap.Height * SizeOf(PRow));
GetMem(ACol, theBitmap.Height * SizeOf(TRGBTriple));
{record the location of the bitmap data:}
for Row := 0 to theBitmap.Height - 1 do
theRows[Row] := theBitmap.Scanline[Row];
{blur each row:}
P := AllocMem(theBitmap.Width * SizeOf(TRGBTriple));
for Row := 0 to theBitmap.Height - 1 do
BlurRow(Slice(theRows[Row]^, theBitmap.Width), K, P);
{now blur each column}
ReAllocMem(P, theBitmap.Height * SizeOf(TRGBTriple));
for Col := 0 to theBitmap.Width - 1 do
begin
{first read the column into a TRow:}
for Row := 0 to theBitmap.Height - 1 do
ACol[Row] := theRows[Row][Col];
BlurRow(Slice(ACol^, theBitmap.Height), K, P);
{now put that row, um, column back into the data:}
for Row := 0 to theBitmap.Height - 1 do
theRows[Row][Col] := ACol[Row];
end;
FreeMem(theRows);
FreeMem(ACol);
ReAllocMem(P, 0);
end;

end.



Example:


procedure TForm1.Button1Click(Sender: TObject);
var
b: TBitmap;
begin
if not openDialog1.Execute then
exit;
b := TBitmap.Create;
b.LoadFromFile(OpenDialog1.Filename);
b.PixelFormat := pf24Bit;
Canvas.Draw(0, 0, b);
GBlur(b, StrToFloat(Edit1.text));
Canvas.Draw(b.Width, 0, b);
b.Free;
end;



راستی اگه می خوای رو موضوع خاصی کار کنی بایستی سیگما که مربوط به kernel گوس و threshold1,threshold2 رو مشخص کنی البته یه روشی واسه تعیین threshold نرمال هم وجود داره اگه خواستی تاپیک بزن.اگه کار عمومیت داره کانی به دردت نمی خوره بایستی hybrid کار کنی یعنی با NN یاFuzzy ترکیبش کنی

مصطفی ساتکی
دوشنبه 30 فروردین 1389, 10:52 صبح
اون بالا فقط guassian blur چون برای انجام لبه canny ابتدا با رو تصویر اونو اعمال کنی

function canny( b: TBitmap ): TBitmap;
var
kv, kh : array[0..2, 0..2] of real;
i, j, k, l : integer;
p : array of PArrRGB;
mag, gx, gy : array of array of real;
bg, br : TBitmap;
dir : real;
tmp, dx, dy : integer;
pr : ParrRGB;
ap : array of TPoint;
edge : array of array of boolean;
pt : array of tpoint;
thrlow, thrhigh : real;

function trace( x, y: integer ): boolean;
var
i, j : integer;

function ok( x, y: integer ): boolean;
begin
result := ( x >= 0 ) and ( y >= 0 ) and ( x < b.Width ) and ( y < b.Height );
end;
begin
result := false;
if not edge[y][x] then exit;

edge[y][x] := false;
result := mag[y][x] >= thrhigh;
for j := -1 to 1 do
for i := -1 to 1 do
if ok( x + i, y + j ) then
result := trace( x + i, y + j ) or result;

//jika hasil rekursi menghasilkan true
//(terhubung dengan piksel yang nilainya lebih besar dari hi-threshold)
if result then begin
pr := br.ScanLine[y];
pr[x] := rgb_hitam;//citra yang baru berwarna latar putih
end;
end;
begin
bg := gaussian( b, 1 );
result := citra_create( b.Width, b.Height );//buat citra(bitmap) baru

setlength( p, b.Height );
for j := 0 to high( p ) do
p[j] := bg.ScanLine[j];

setlength( gx, b.Height );
for j := 0 to high( gx ) do
setlength( gx[j], b.Width );

setlength( gy, b.Height );
for j := 0 to high( gx ) do
setlength( gy[j], b.Width );

setlength( mag, b.Height );
for j := 0 to high( mag ) do
setlength( mag[j], b.Width );

setlength( edge, b.Height );
for j := 0 to high( edge ) do
setlength( edge[j], b.Width );

//hitung gradien gx dan gy menggunakan operator sobel
for j := 1 to b.Height - 2 do begin
for i := 1 to b.Width - 2 do begin
gy[j][i] := ( p[j + 1][i - 1].r - p[j - 1][i - 1].r ) + 2 * ( p[j + 1][i].r - p[j - 1][i].r ) + ( p[j + 1][i + 1].r - p[j - 1][i + 1].r );
gx[j][i] := ( p[j - 1][i + 1].r - p[j - 1][i - 1].r )
+ 2 * ( p[j][i + 1].r - p[j][i - 1].r )
+ ( p[j + 1][i + 1].r - p[j + 1][i - 1].r );
mag[j][i] := sqrt( gx[j][i] * gx[j][i] + gy[j][i] * gy[j][i] );
end;
end;

//non-maximal suppression
for j := 1 to b.Height - 2 do begin
for i := 1 to b.Width - 2 do begin
dir := arctan2( gy[j][i], gx[j][i] );
dx := round( cos( dir ) );
dy := round( sin( dir ) );

edge[j][i] := ( mag[j][i] >= mag[j + dy][i + dx] ) and ( mag[j][i] >= mag[j - dy][i - dx] );
if edge[j][i] then begin
edge[j + dy][i + dx] := false;
edge[j - dy][i - dx] := false;
end;
end;
end;

//apply hysteresis
thrlow := 10;
thrhigh := 90;
br := result;
for j := 1 to b.Height - 2 do begin
for i := 1 to b.Width - 2 do begin
if edge[j][i] then trace( i, j );//trace melakukan edge-linking
end;
end;
end;

مصطفی ساتکی
دوشنبه 30 فروردین 1389, 11:21 صبح
کانی اول اینو روی سیگنال یه بعدی پیاده کرد و بعد آوردش رو 2 بعد(تصویر).به این خاطر از فیلتر پایین گذر گوس استفاده کرد تا نویز موجود در تصویرو از بین ببره.ابتدا تصویرتو gray scale کن روش gaussian blur رو اعمال کن بعد اون بایستی از متشق مرتبه اول یا فیلتر sobel استفاده کنی
gx=[-1,-2,-1
0,0,0
1,2,1
gy=[-1,0,1
-2,0,2
-1,0,1
gx, gy رو کانولوشن می کنی رو هر نقطه پس gx ,gy بدست می آید حالا بایستی شدت رو محاسبه کنی G=sqrt(sqr(gx)+sqr(gy))
g همون مقدار شدت محاسبه شده است.
برای بدست آوردن زوایه مابین gx,gy از teta=arctan(gy/gx) می کنه این زوایه شدت در هر نقطه بهت می ده .سپس با یستی این زوایایه بدست آمده رو round کنی خلاصه کلام چون kernel 3*3 می خایم تعیین کنیم زوایه در راستای کدوم چهر جهت اصلیه.
0و90و180 یا 270
پس از پیدا کردن بایست عمل non-maximal suppression رو انجام بدی همون طور که از اسمش پیدا یعنی هر چی غیر از جهت شدت باشه حذف می کنه بعد این بایستی hysteresis رو اعمال کنی چون تا الان اگه تصویر رو ببینی لبه خیلی ضخیمن پس بایستی لبه ها رو نازک کنی. مثل یه trace ساده نو 8 خونه مجاور یه نقطه. اون 2 تا Threshold که گفتم اگه یادت نرفته اینجا به دردت می خوره هر نقطه که پایین تر از threshold1 باشه 0 (BackColor) و هر نقطه بالاتر از threshold2 باشه 255 (EdgeColor) می شه هر نقطه هم که مابین این دو threshold باشه اگر با یک نقطه ای شدتش بزرگتر مساوی threshold2 به 255 تبدیل می شه و گرنه صفر می شه
اینو می گن الگوریتم لبه کانی به همین راحتی

asefy2008
جمعه 17 اردیبهشت 1389, 15:34 عصر
با یک دنیا تشکر از راهنمایی هاتون ولی یه سوال داشتم :
gaussian blur رو چطور میشه اعمل کرد (توضیح :خجالت:)

مصطفی ساتکی
جمعه 17 اردیبهشت 1389, 16:29 عصر
gaussian blur ابتدا یک kernel تولید میکنه و سپس این kernel رو بروی کل تصویر اعمال می کنه کد اول مربوط به اونه .این kernel در حالت 3 بعدی به شکل کلاه مکزیکیه.در واقع به این خاطر از این فیلتر پایین گذر استفاده می کنه که نویز موجود در تصویر رو کاهش بده. اگر شما بخاید Realtime کار کنید حتماً نبایست از Kernel گوس استفاده کنید یه فیلتر معمولی هم میشه استفاده کرد .
خبر جدید : من فیلتر لبه ای رو با شبکه SOFM تولید کردم که جواب خیلی خوبی رو نسبت به canny می ده و Realtime عمل می کنه. Canny به چندین پارامتر وابسته است و این مشکل threshold1,threshold2 و همین Sigma که میزان تاثیر فیلتر پایین گذر رو مشخص می کنه.Canny در همهئ شرایط نمی تونه جواب مطلوب رو به شما بده.

asefy2008
شنبه 18 اردیبهشت 1389, 01:28 صبح
نمی دونم درست فهمیدم یا نه. میگم اگر اشتباه بود لطف کن و اصلاح کن :
gaussian blur یک آرایه 3*3 به ما میده که ما اون رو کل تصویر اعمال کنیم .
این آرایه از طریق مشتق گیری از آرایه زیر بدست میاد:
1 2 1
2 4 2
1 2 1

اگر اشتباه گفتم لطفا کامل توضیح بده چون سورس gaussian blur رو به زبان سی شارپ دانلود کردم بازم متوجه نشدم (البته سورسش اجرا نمی شد ولی شبیه کد شما بود.)

asefy2008
شنبه 18 اردیبهشت 1389, 01:29 صبح
راستی یادم رفت بگم دارم روی تشخیص هویت از طریق عنبیه کار می کنم.

مصطفی ساتکی
شنبه 18 اردیبهشت 1389, 16:29 عصر
نه الزاماً 3*3 می تونه هر ابعادی داشته باشه و این مقدار بستگی به Sigma داره که میزان انتشار رو مشخص مینکه.بعلت محاسبات زمانگیر Guassian blur مقدار اون را از یک حد به بالاتر نادیده می گیرند چون این اعداد حذف شده اعشاری بوده و محاسباتشون زمانگیر و البته تاثیر زیادی هر مبر خروجی نداره .این کدی رو که در بالا براتون گذاشتم برای اینکه سرعت محاسبات بالاتر باشه مثل FFT عمل میکنه.یعنی بجای اینکه درجا به صورت 2 بعدی عمل کنه 2 بار به صورت یک بعدی عمل میکنه.یکبار Kernel مد نظر مون که یک آرایه متقارن بروی سطر ها اعمال میکنه و بار دیگه بروی ستون ها.شما برای اینکه راحتر متوجه بشید Function مربوط Gblur رو تیکه تیکه اجرا کند تا خروجی هر قسمت رو بروی تصویر مقصدتون ببینید. مقادیر آرایه Kernel از قبل تعیین شده نیست و با مشخص نمودن sigma محاسبه میشه

alimooghashang
دوشنبه 19 مهر 1389, 20:12 عصر
gaussian blur ابتدا یک kernel تولید میکنه و سپس این kernel رو بروی کل تصویر اعمال می کنه کد اول مربوط به اونه .این kernel در حالت 3 بعدی به شکل کلاه مکزیکیه.در واقع به این خاطر از این فیلتر پایین گذر استفاده می کنه که نویز موجود در تصویر رو کاهش بده. اگر شما بخاید Realtime کار کنید حتماً نبایست از Kernel گوس استفاده کنید یه فیلتر معمولی هم میشه استفاده کرد .
خبر جدید : من فیلتر لبه ای رو با شبکه SOFM تولید کردم که جواب خیلی خوبی رو نسبت به canny می ده و Realtime عمل می کنه. Canny به چندین پارامتر وابسته است و این مشکل threshold1,threshold2 و همین Sigma که میزان تاثیر فیلتر پایین گذر رو مشخص می کنه.Canny در همهئ شرایط نمی تونه جواب مطلوب رو به شما بده.
این روشی که شما معرفی کردید از canny بهتره؟
من اطلاعاتی رو در مورد canny میخوام، چون با OpenCV میخوام کار کنم و توسط الگوریتم canny لبه گیری میکنه ولی من نمیدونم پارامتر های threshold رو چی بدم که جوابی که میخوام رو بهم بده!
ممنونم میشم در این زمینه کمکم کنید!

مصطفی ساتکی
سه شنبه 20 مهر 1389, 07:45 صبح
تعیین threshold بستگی به میزان روشنایی و نویز بستگی داره که با توجه به فیلدی که در اون کار می کنید این پارامتر set میشه .اگر مایلید که این پارامتر ها به صورت هوشمندانه تعیین شوند یعنی با توجه به نور محیط میزان نویز با یستی از cnn استفاده کنید که در opencv همچین چیزی وجود نداره اگر هم کارتون تجاریه با من تماس بگیرید.
موفق باشید

hanieh sarbazi
پنج شنبه 23 تیر 1390, 17:53 عصر
سلام بچه ها من فیلترهای پردازش تصویر مثل سابل و گاوسین و لاپلاس رو نیاز دارم میشه خواهش کنم کدهاش رو بذارید

مصطفی ساتکی
جمعه 24 تیر 1390, 00:00 صبح
البته سوالي كه شما پرسيد بايستي براش يه تاپيك ايجاد مي كرديد.

اين 3 مورد رو كه شما نياز داريد تحت عنوان فيلتر در image processing و singnal processing استفاده ميشن.در واقع با اعمال فيلتر ها در تصوير ماهيتشون رو به گونه اي تغيير ميدم كه بتونيم از تصوير بدست آمده feature هاي مورد نياز رو extract كنيم.
در واقع براي اعمال فيلتر شما بايستي يك ماتريس رو در كل تصوير ضرب كنيد كه به اين عمل convolution گفته ميشه.
اين ماتريسي كه در تصوير اصلي ضرب ميشه اصطلاحا kernel نام داره كه به ابعاد اون support size گفته ميشه.كه در اغلب كاربردها اين ابعاد برابره مثل 3*3 يا 5*5 هر چقدر اين اندازه بزرگتر باشه شما neighbor هاي بيشتري رو در بدست آوردن مقدار intensity پيكسل جاري شركت ميديد.
مثلاً فيلتر اولي كه گفتيد sobel در 2 جهت بر روي تصوير اعمال ميشه x,y .
فيلتر در واقع همون مشتق مرتبه اول در راستاي x و y .(گراديان) مشتق در واقع ميزان اختلاف رو به ما نشون ميده.
لاپلاسين يعني حاصل جمع مشتق مرتبه دوم در راستاي x و y.
شما در لبه اشيا ميزان intensity اختلاف زيادي داره كه در واقع با اعمال گراديان و لاپلاسين اين لبه ها نمايان ميشه.
البته نا گفته نمونه كه شما مي تونيد از orientation و magnitude گراديان هم استفاده كنيد. در ضمن گراديان و لاپلاسين بالاگذرند
يه فيلتر ديگه كه فرموديد gaussian هستش كه يه فيلتر پاپپن گذر .در واقع يكجور هموار كننده كه عمل smooth كردن رو انجام ميده. در مواردي كه شما در تصويرتون نويز داريد با اعمال gaussian اين نويز ها در تصوير از بين ميرن. البته اين كاربردش در تصويره در signal هم كاربردهاي متنوعي داره.
مثلا يه مثال signal براتون بزنم شما زمانيكه در يك جريان صوتي صداي foreground قطع شده صداي background مثل
صداي باد به گوش ميرسه شما كافي اين سيگنال رو gaussian بگيريد و از خودش كسر كنيد صداي باد از بين ميره.

براي اعمال فيلتر ها به صورت استاتيك و در support size مشخص مثلاً 3*3 مي تونيد به صورت زير عمل كنيد.
sobelHorizontal[3][3] = {1, 2, 1, 0, 0, 0, -1, -2, -1 };
sobelVertical[3][3] = {1, 0, -1, 2, 0, -2, 1, 0, -1 }
gaussian[3][3] = {1, 2, 1, 2, 4, 2, 1, 2, 1}
Laplacian [3][3] = {-1, -1, -1, -1, 8, -1, -1, -1, -1 };

كه توضيحاتي كه در بالا ارائه شد در حالت spatial بود كه همين فيلتر ها رو تو حوزه
فركانس هم ميشه اعمال كرد