فصل دوم - حوزهٔ مکان
نگاشت
در این بخش به چگونگی استفاده از تابع remap برای پیاده سازی نگاشتهای ساده میپردازیم.
به فرایند تغییر مکان پیکسلهای یک تصویر و بردن آنها به مکانهای دیگر در یک تصویر جدید، نگاشت می گویند. میتوان عملیات نگاشت هر پیکسل $(x,\ y)$ را به صورت زیر نشان داد:
$$g\left( x,\ y \right) = f\left( h\left( x,\ y \right) \right)$$
که $g$ تصویر نگاشت یافته، $f$ تصویر منبع و $h$ هم تابع نگاشت است.
به عنوان مثال فرض کنید عکسی به نام I داریم و میخواهیم یک نگاشت به صورت زیر روی آن اعمال کنیم:
$$h\left( x,\ y \right) = \left( I.cols - x,\ y \right)$$
به راحتی میتوان دید که نتیجهٔ این نگاشت چرخیدن عکس در جهت x است. مثلاً عکس زیر را در نظر بگیرید:
![]() |
![]() |
---|---|
تصویر نگاشت شده | تصویر اصلی |
به تغییر موقعیت دایره قرمز نسبت به X توجه کنید (با توجه به اینکه x جهت افقی است).
کد
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace cv;
/// Global variables
Mat src, dst;
Mat map_x, map_y;
char* remap_window = "Remap demo";
int ind = 0;
/// Function Headers
void update_map( void );
/**
* @function main
*/
int main( int argc, char** argv )
{
/// Load the image
src = imread( argv[1], 1 );
/// Create dst, map_x and map_y with the same size as src:
dst.create( src.size(), src.type() );
map_x.create( src.size(), CV_32FC1 );
map_y.create( src.size(), CV_32FC1 );
/// Create window
namedWindow( remap_window, CV_WINDOW_AUTOSIZE );
/// Loop
while( true )
{
/// Each 1 sec. Press ESC to exit the program
int c = waitKey( 1000 );
if( (char)c == 27 )
{ break; }
/// Update map_x & map_y. Then apply remap
update_map();
remap( src, dst, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );
/// Display results
imshow( remap_window, dst );
}
return 0;
}
/**
* @function update_map
* @brief Fill the map_x and map_y matrices with 4 types of mappings
*/
void update_map( void )
{
ind = ind%4;
for( int j = 0; j < src.rows; j++ )
{ for( int i = 0; i < src.cols; i++ )
{
switch( ind )
{
case 0:
if( i > src.cols*0.25 && i < src.cols*0.75 &&
j > src.rows*0.25 && j < src.rows*0.75 )
{
map_x.at<float>(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ;
map_y.at<float>(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ;
}
else
{ map_x.at<float>(j,i) = 0 ;
map_y.at<float>(j,i) = 0 ;
}
break;
case 1:
map_x.at<float>(j,i) = i ;
map_y.at<float>(j,i) = src.rows - j ;
break;
case 2:
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = j ;
break;
case 3:
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = src.rows - j ;
break;
} // end of switch
}
}
ind++;
}
توضیح
در خط 44 برای انجام نگاشت، تابع remap را صدا میزنیم. این تابع پنج آرگومان به شرح زیر دارد:
- src: تصویر ورودی
- dst: تصویر خروجی که همان اندازهٔ src است.
- map_x: تابع نگاشت در جهت x است. معادل قسمت اول تابع $h\left( i,\ j \right)$ است.
- map_y: مشابه بالایی، فقط در جهت y است. توجه کنید که map_x و map_y هر دو هم اندازهٔ src هستند.
- CV_INTER_LINEAR: نوع دورن یابی ای که برای پیکسلهای غیر صحیح به کار میرود. پیش فرض آن همین مقدار است.
- BORDER_CONSTANT: نوع قابی که به اطراف تصویر اضافه میشود.
در این برنامه چهار نوع نگاشت به تصویر ورودی اعمال میشود. تابع $h\left( i,\ j \right)$ هر کدام از این نگاشتها به صورت زیر است:
i. کاهش اندازهٔ تصویر به نصف و نشان دادن آن در مرکز:
$$h\left( i,\ j \right) = \left( 2*i - \frac{\text{src.cols}}{2} + 0.5,\ 2*j - \frac{\text{src.rows}}{2} + 0.5 \right)$$
برای همهٔ زوجهای $\left( i,\ j \right)$ به صورتی که:
$$\frac{\text{src.cols}}{4} < i < \frac{3*src.cols}{4}\text{\ and}\frac{\text{src.rows}}{4} < j < \frac{3*src.rows}{4}$$
ii. بالا به پایین کردن تصویر: $h\left( i,\ j \right) = \left( i,\ src.rows - j \right)$
iii. چپ به راست کردن تصویر: $h\left( i,\ j \right) = \left( \ src.cols - i,\ j \right)$
iv. ترکیب ii و iii: $h\left( i,\ j \right) = \left( \ src.cols - i,\ \ src.rows - j \right)$
تمام موارد بالا در خطوط 65 تا 88 پیاده سازی شدهاند. در اینجا map_x نشان دهندهٔ مختص اول $h\left( i,\ j \right)$ و map_y مختص دوم آن است.
خروجی
![]() |
![]() |
---|---|
تصویر ورودی | بالا به پایین کردن تصویر ورودی |
![]() |
![]() |
---|---|
چپ به راست کردن تصویر ورودی | بالا به پایین و چپ به راست کردن تصویر ورودی |
![]() |
---|
کاهش اندازهٔ تصویر به نصف و نشان دادن آن در مرکز |