در این قسمت ساختار های داده پایه، ماتریس ها و دیگر ساختار های مورد نیازدر پروژه ها را معرفی می کنیم. همچنین در مورد چگونگی ذخیره متغیر ها و داده ها در فایل مشابه به وسیله توابع OpenCV صحبت خواهد شد.
- خواندن و نوشتن عکس
- خواندن ویدئو و دسترسی به دستگاه های دوربین
- ساختار های تصویر اصلی و دیگر ساختار های مهم پایه در ماتریس
آنچه در این مطلب خواهیم خواند :
Toggleتصویر کردن و ماتریس ها
بدون شک مهم ترین ساختار در بینایی ماشین عکس ها می باشند. عکس در بینایی ماشین نمایش دنیای فیزیکی ذخیره شده به وسیله ی دستگاه دیجیتالی است. هر عکس فقط شامل مجموعه ای از اعداد ذخیره شده می باشد(مانند آبی، سبز و قرمز) که هر نقطه عکس پیکسل نامیده می شود. هر پیکسل می تواند یک یا تعداد بیشتری مقدار را بسته به نوع عکس ذخیره نماید. مثلا پیکسل عکس خاکستری یک مقدار را ذخیره می کند و در عکس رنگی هر پیکسل شامل سه مقدار است.
این مقدار معمولا یک عدد صحیح بین ۰ تا ۲۵۵ هستند ولی می توانند مقادیر دیگری هم داشته باشند. مانند ۰ تا ۱ برای اعداد اعشاری.
عکس به صورت ماتریس است و هر مقدار به وسیله سطر و ستونی مشخص می شود. برای این منظور در OpenCV از کلاس Mat استفاده میکنیم. برای عکس خاکستری یک ماتریس و برای عکس رنگی سه ماتریس داریم.
W*H*N
W عرض تصویر، H ارتفاع تصویر و N تعداد کانال های رنگ تصویر(۱ کانال برای عکس خاکستری و ۳ کانال برای عکس رنگی)
کلاس Mat فقط برای ذخیره عکس استفاده نمی شود بلکه ماتریس هایی با اندازه دلخواه و مقدار دلخواه را نیز ذخیره می کند و شما می توانید اعمال جبری را بر روی این ماتریس ها انجام دهید. ماتریس ها در حافظه ذخیره می شوند. در دسترسی موثر به جای استفاده از تابع در حافظه، ماتریس به عنوان یک آرایه یا توالی از مقادیر که به وسیله سطر و ستون مرتب شده اند ذخیره می شود. توالی پیکسل ها در OpenCV به صورت BGR Pixel است.
دسترسی به پیکسل ها به وسیله فرمت پیش رو میباشد:
Value = Raw – i * num-col*numchannels + coli + channel –i
توابع OpenCV برای دسترسی تصادفی کاملا بهینه شده اند ولی گاهی اوقات دسترسی مستقیم به حافظه (کار با حساب اشاره گر(Pointer Arithmatic)) کار آمد تر است. مثلا زمانی که می خواهیم به تمامی پیکسل ها در یک حلقه دسترسی داشته باشیم.
خواندن/نوشتن عکس ها
بعد از مقدمه گفته شده درباره ماتریس، ما ادامه آموزش را با یک کد ساده OpenCV ادامه میدهیم. در ابتدا باید بدانیم که عکسها را چگونه بخوانیم و ذخیره کنیم.
#include
#include
#include
using namespace std;
// OpenCV includes
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main( int argc, const char** argv )
{
// Read images
Mat color= imread("../color.jpg");
Mat gray= imread("../color.jpg", 0);
// Write images
imwrite("color.jpg", gray);
// get same pixel with opencv function
int myRow=color.cols-1;
int myCol=color.rows-1;
Vec3b pixel= color.at(myRow, myCol);
cout << "Pixel value (B,g,R): (" << (int)pixel[0] << "," <<
(int)pixel[1] << "," << (int)pixel[2] << ")" << endl;
// show images
imshow("color BgR", color);
imshow("color gray", gray);
// wait for any key press
waitKey(0);
return 0;
}
خب بیایید کد را با هم بررسی کنیم :
// Read images
Mat color= imread("../color.jpg");
Mat gray= imread("../color.jpg", 0);
تابع imread تابع اصلی برای خواندن عکس ها است. این تابع عکس را باز کرده و به فرمت ماتریس ذخیره می کند. پارامتر اول رشته ای است که مسیر عکس می باشد و پارامتر دوم اختیاری است که به صورت پیش فرض عکس را رنگی بارگذاری می کند. پارامتر دوم به صورت زیر است:
CV_Load_Image_Anydepth
اگر بدین صورت کد نویسی کنیم زمانی که ورودی عمق متناظر را دارد تصویر یک عکس ۱۶-۳۲ بیتی می باشد در غیر این صورت تابع imread عکس را به ۸ بیت تبدیل می کند.
CV_Load_Image_Color
اگر به این صورت تنظیم شود عکس به صورت رنگی تبدیل می کند.
CV_Load_Image_Grayscale
اگر این مقدار ثابت تنظیم شود، همیشه عکس را به grayscale تبدیل میکند.
ذخیره عکس Imwrite
برای ذخیره تصویر در کامپیوتر از imwrite استفاده می کنیم.
// Write images
imwrite("colorgray.jpg", gray);
پارامتر اول مسیر و فرمتی که ما می خواهیم ذخیره کنیم و پارامتر دوم عکسی است که می خواهیم ذخیره شود. در نمونه کد ما، ما ابتدا یک نمونه خاکستری از عکس ساخته و آن را ذخیره میکنیم و سپس با فرمت JPG عکس خاکستری را که در متغییر gray بارگذاری کردیم ذخیره میکنیم.
دسترسی به پیکسل های عکس
// get same pixel with opencv function
int myRow=color.cols-1;
int myCol=color.rows-1;
با استفاده از خصوصیات .raws و .cols می توانیم به تعداد سطر و ستون های ماتریسی دسترسی داشته باشیم.
Vec3b pixel= color.at(myRow, myCol);
cout << "Pixel value (B,g,R): (" << (int)pixel[0] << "," << (int)
pixel[1] << "," << (int)pixel[2] << ")" << endl;
برای دسترسی به یک پیکسل از یک عکس ما از کلاس Mat OpenCV CV::Mat::at(row, col) استفاده می کنیم. پارامتر typename در عکس رنگی ۸ بیتی یک کلاس به نام Vec3b میباشد که Unsigned Char را ذخیره میکند. Vec = vector)، ۳=تعداد کامپوننت و b=1byte). برای عکس خاکستری، میتوانیم مستقیما از uchar یا تعداد فرمتهای دیگری که در عکس استفاده میشود، مانند uchar pixel= color.at(myRow, myCol) استفاده کنیم.
درنهایت، برای نمایش عکسها، میتوانیم از تابع imshow برای ساخت پنجره با عنوان پنجره که پارامتر اولش و پارامتر دوم ماتریس عکس میباشد استفاده میکنیم.
// show images
imshow("color BgR", color);
imshow("color gray", gray);
// wait for any key press
waitKey(0);
اگر ما بخواهیم برنامه را متوقف کنیم به طوری که با فشردن کلیدی توسط کاربر متوقف شود از Waitkey استفاده می کنیم که پارامتر آن زمان صبر کردن بر حسب میلی ثانیه می باشد. و اگر صفر وارد کنیم تا ابد صبر خواهد کرد.
نتیجه نهایی کد در پایین نشان داده شده است که در عکس، سمت چپ رنگی و عکس سمت را خاکستری است.
Other basic object types
در اینجا دیگر انواع پایه ای مورد نیاز در پروژه ها را معرفی می کنیم.
وکتور vector
vec یک کلاس کلی است که اصطلاحا برای بردار های عددی مورد استفاده قرار می گیرد. ما هر نوع بردار و یک تعداد —- را میتوانیم تعریف کنیم.
Vec < double , 19> myvector
یا میتوانیم از دیگر انواع تعریف شده استفاده کنیم.
تمام عملیاتهای برداری مورد انتظار به صورت زیر اجرا میشود.
typedef Vec Vec2b;
typedef Vec Vec3b;
typedef Vec Vec4b;
typedef Vec Vec2s;
typedef Vec Vec3s;
typedef Vec Vec4s;
typedef Vec Vec2i;
typedef Vec Vec3i;
typedef Vec Vec4i;
typedef Vec Vec2f;
typedef Vec Vec3f;
typedef Vec Vec4f;
typedef Vec Vec6f;
typedef Vec Vec2d;
typedef Vec Vec3d;
typedef Vec Vec4d;
typedef Vec Vec6d;
v1 = v2 + v3
v1 = v2 - v3
v1 = v2 * scale
v1 = scale * v2
v1 = -v2
v1 += v2
v1 == v2, v1 != v2
norm(v1) (euclidean norm)
اسکالر scalar
نوع بعدی scalar است که از کلاس vec مشتق شده و دارای ۴ المان است. scalar به طور زیادی برای خواندن و فرستادن مقدار پیکسل ها استفاده می شود. برای دسترسی به مقدار پیکسل ها از عملگر [] استفاده می کنیم.
نقطه Point
کلاس رایج دیگر point میباشد . این کلاس یک نقطه ۲d را تعریف می کند که به وسیله مختصات X و y مشخص می شود. برای نقطه ۳d هم از point3استفاده میکنیم.
همانند کلاس Vec، OpenCV انواع مختلفی از این کلاس را برای ما تعریف میکند.
typedef Point_ Point2i;
typedef Point2i Point;
typedef Point_ Point2f;
typedef Point_ Point2d;
عملیات های پیش رو برای نقطه تعریف شده است.
pt1 = pt2 + pt3;
pt1 = pt2 - pt3;
pt1 = pt2 * a;
pt1 = a * pt2;
pt1 = pt2 / a;
pt1 += pt2;
pt1 -= pt2;
pt1 *= a;
pt1 /= a;
double value = norm(pt); // L2 norm
pt1 == pt2;
pt1 != pt2;
اندازه size
کلاس بعدی size می باشد که برای مشخص کردن سایز یک عکس یا مستطیل استفاده می شود. دو عضو width و height دارد و تابع مفیدی به نام area() را شامل می شود.
مستطیل Rect
کلاس کلی دیگری که مورد استفاده قرار می گیرد Rect است که یک مستطیل دو بعدی با پارامتر های زیر تعریف می شود.
- مختصات گوشه بالا و سمت چپ
- اندازه طول و عرض مستطیل
از کلاس کلی Rect برای تعریف ROI (region of intrest) استفاده می شود.
مستطیل چرخیده RotatedRect
کلاس بعدی RotatedRect است که به وسیله مرکز، اندازه مستطیل و زاویه گوشه مشخص می شود.
RotatedRect(const Point2f& center, const Size2f& size, float angle);
یکی تابع این کلاس boundingbox است که یک rect را شامل این مستطیل چرخیده برمی گرداند.