12 Eylül 2017 Salı

OpenCV Mat Sınıfı

Giriş
Şu satırı dahil ederiz.
#include <opencv2/opencv.hpp>` 
Açıklaması şöyle
Mat is basically a class with two data parts: the matrix header (containing information such as the size of the matrix, the method used for storing, at which address is the matrix stored, and so on) and a pointer to the matrix containing the pixel values (taking any dimensionality depending on the method chosen for storing). The matrix header size is constant, however the size of the matrix itself may vary from image to image and usually is larger by orders of magnitude.
.h ve .cpp dosyası
Sınıfın iskeleti mat.hpp dosyasında . Kodu ise matrix.cpp dosyasında. Dosya isimleri klasik foo.h ve foo.cpp kullanımına uymuyor.

Constuctor - rows + cols + type
Type kaç tane kanal olduğunu belirtir. Açıklaması şöyle. RGB 3 kanaldır.
OpenCV matrices are stored in interleaved format. That means for a two channel image with channels A and B, a 4x1 matrix would be stored in the order ABABABAB.
Interleaved yani içiçe geçmiş veri ayrı olarak düşünürsek aslında şöyledir.
a c e g i
 b d f h
Şöyle yaparız.
cv::Mat image (1, 256, CV_8UC3);
Şu type kullanımında (CV_32F) başka resimden kopyalarken kaç tane kanal olduğu belirtilmediği için doğru değil. CV_32FC3 gibi bir sayı kullanılmalıydı
cv::Mat image = cv::Mat(depth.rows, depth.cols, CV_32F);
Constuctor - rows + cols + type + scalar
Metodun imzası şöyle. Yani resmin büyüklüğünü, tipini ve piksel değerlerini vermek gerekir.
cv::Mat(int rows, int cols, int type, SomeType initialValue);
Şöyle yaparız.
cv::Mat image (rows,cols,CV_32F,5.0);
Benzer bir resim şöyle de oluşturulabilirdi.
cv::Mat image = cv::Mat(512,512,CV_8UC1, cv::Scalar(0));
Ya da şöyle oluşturulabilirdi.
cv::Mat image (rows,cols,CV_32F, cv::Scalar::all(0) );
Constructor - Data
Metodun imzası şöyle
cv::Mat(int rows, int cols, int type, char* preAllocatedPointerToData);
Şöyle yaparız. Mat sınıfı dışarından verilen veriyi kopyalamaz ve silmez!
void *data = ...;
cv::Mat image (rows, cols, CV_8UC1, data);
Float dizisi için şöyle yaparız.
float data[9] = ...;
cv::Mat image (3, 3, CV_32F, &data[0]);
Şöyle yaparız.
float data[10] = {1,2,3,4,5,7,8,9,10,11};
cv::Mat A (1, 10, cv::CV_32FC1, data);
Constructor - vector
Şöyle yaparız.Tek sütunlu vector'ün uzunluğu kadar satır sayısına sahip bir nesne yaratır.
std::vector<cv::Point> > v;
cv::Mat img (v);
at metodu - getter - row + colum
Kartezyen sistemde x,y şeklinde kullanım geleneksel. Ancak Mat sınıfında row + colum şeklinde kullanılıyor. Açıklaması şöyle
Following conventional matrix notation, rows are numbered by the first index of a two-dimensional array and columns by the second index, i.e., a1,2 is the second element of the first row, counting downwards and rightwards. (Note this is the opposite of Cartesian conventions.)
Nesne CV_8UC3 ise BGR döner. Şöyle yaparız.
Vec3b v = image.at<Vec3b>(y, x);
uchar& b = v[0];
uchar& g = v[1];
uchar& r = v[2];
Nesne CV_8UC4 ise BGRA döner. Şöyle yaparız.
Vec4b v = image.at<Vec4b>(y, x);
İlk parametre row, ikinci parametre column olduğu için döngü içinde kullanmak için şöyle yaparız.
int w=img.cols;
int h=img.rows;

for(int i=0;i<img.rows;++i)
{
  for(int j=0;j<img.cols;++j)
  {   
  
    Vec3b b=img.at<Vec3b>(i,j);
    ...
  }
}
at metodu - setter - row + colum
Örnek
CV_8UC1 için şöyle yaparız.
image.at<uchar>(y,x) = 255;
Örnek
CV_8UC3 için şöyle yaparız.
cv::Vec3b color = ...
image.at<cv::Vec3b>(j,i) = color;
Örnek
CV_64FC1 için şöyle yaparız.
image.at<double>(0,0)=  1.01121;
begin ve end iterator metodları
begin() ve end() iterator metodları yerine ptr () metodu da kullanılabilir.
Örnek
Şöyle yaparız.
Mat image = ...;
// the loop below assumes that the image
// is a 8-bit 3-channel. check it.
CV_Assert(image.type() == CV_8UC3);
MatConstIterator_<Vec3b> it = image.begin<Vec3b>(),
                     it_end = image.end<Vec3b>();
for( ; it != it_end; ++it )
{
  const Vec3b& pix = *it;
  ...
}
Örnek
Şöyle yaparız.
cv::Mat mat = ...;

char table[mat.rows * mat.cols * 3]; // assuming that we have 3 colour channels
unsigned int index = 0;

cv::MatIterator_<uchar> it, end; 
for( it = mat.begin<uchar>(), end = mat.end<uchar>(); it != end; ++it) 
  table[index++] = *it;
Örnek
Şöyle yaparız
cv::MatIterator_<cv::Vec3b> it;
for (it = mat.begin<cv::Vec3b>(); it != mat.end<cv::Vec3b>(); ++it)
{
  (*it)[0] = 0;
  (*it)[1] = 0;
}
channel metodu
Şöyle yaparız. Siyah beyaz resimler için channel değeri 1 döner.
if (image.channels() == 3) {...}
clone metodu
Şöyle yaparız.
Mat srcColor = ...; Mat dst = srcColor.clone();
col metodu
Şöyle yaparız.
cv::Mat data = (cv::Mat_<float>(2, 4) << 1.f, 2.f, 3.f, 4.f,
                                         5.f, 6.f, 7.f, 8.f);
cv::Mat res(data.size(), data.type());
for (int i = 0; i < data.cols; ++i) {
    cv::normalize(data.col(i), res.col(i), i+1, 0, cv::NORM_INF);
}
std::cout << res;
cols Alanı
rows Alanı kısmına bakınız.

convertTo metodu
Resmi double veriden float veriye vs. çevirir. Şöyle yaparız.
cv::Mat data = cv::Mat::ones(2, 2, CV_16SC1);

cv::Mat converted;
data.convertTo(converted, CV_32FC1);
Double veriye çevirmek için şöyle yaparız.
cv::Mat inputImage = cv::imread("testImage.png");
cv::Mat fImage; 
inputImage.convertTo(fImage, CV_64F); 
copyTo metodu - destination
Şöyle yaparız.
Mat srcImage = ...;
Mat dstImage;
srcImage.copyTo (dtImage);
copyTo metodu - destination + mask
Belirtilen maskeye uyan alanı diğer resme kopyalar. Şöyle yaparız.
cv::Mat srcImage = ...;

//Then define your mask image
cv::Mat mask = cv::Mat::zeros(srcImage.size(), srcImage.type());

//Define your destination image
cv::Mat dstImage = cv::Mat::zeros(srcImage.size(), srcImage.type());    

//draw the circle at the center of your image, with a radius of 50
cv::circle(mask, ...);

//copy source image to destination image with masking
srcImage.copyTo(dstImage, mask);
elemSize metodu
Tüm nesnenin kaç byte olduğunu bulmak için şöyle yaparız.
int imgSize = img.total() * img.elemSize();
empty metodu
Şöyle yaparız.
if(image.empty()) {...}
get metodu
Java ile şöyle yaparız.
Mat mat = ...;
int cols = mat.cols();
int rows = mat.rows ();

int [] dat = new int [cols * rows];

mat.get(0, 0, dat);
isContinuous metodu
Açıklaması şöyle
The method returns true if the matrix elements are stored continuously without gaps at the end of each row. Otherwise, it returns false. Obviously, 1x1 or 1xN matrices are always continuous. Matrices created with Mat::create() are always continuous. But if you extract a part of the matrix using Mat::col(), Mat::diag() , and so on, or constructed a matrix header for externally allocated data, such matrices may no longer have this property.
create() metodunu direkt veya dolaylı yoldan çağıran metodlar boşluksuz bellek kullanır.
Örnek
Şöyle yaparız.
// Read image
Mat img = imread("path_to_image");
cout << "img is continuous? " << img.isContinuous() << endl; 
// Yes, calls create internally

// Constructed a matrix header for externally allocated data
Mat small_mat = img.col(0);
cout << "small_mat is continuous? " << small_mat.isContinuous() << endl; 
// No, you're just creating a new header.

// Matrix (self) expression
small_mat = small_mat + 2;
cout << "small_mat is continuous? " << small_mat.isContinuous() << endl; 
// No, you're not even creating a new header

// Matrix expression
Mat expr = small_mat + 2;
cout << "expr is continuous? " << expr.isContinuous() << endl; 
// Yes, you're creating a new matrix

// Clone
Mat small_mat_cloned = img.col(0).clone();
cout << "small_mat_cloned is continuous? " << small_mat_cloned.isContinuous()
<< endl; 
// Yes, you're creating a new matrix

// Create
Mat mat(10, 10, CV_32FC1);
cout << "mat is continuous? " << mat.isContinuous() << endl; 
// Yes, you're creating a new matrix
ones metodu
Şöyle yaparız.
cv::Mat data = cv::Mat::ones(2, 2, CV_16SC1);
operator Rect metodu
Submatrix almak için kullanılır. Şöyle yaparız.
cv::Mat img = cv::imread("Lenna.png");

cv::Rect roi(128, 128, 256, 256);
cv::Mat submat = img(roi);
push_back metoud
Açıklaması şöyle
The methods add one or more elements to the bottom of the matrix. They emulate the corresponding method of the STL vector class. When elem is Mat , its type and the number of columns must be the same as in the container matrix.
Eklenen satır veya matris orijinal matris ile aynı sütun genişliğine sahip olmalı. Şöyle yaparız.
Mat mat1= ...;
Mat mat2 = ...;

mat1.push_back(mat2);

ptr metodu
Belirtilen indeksteki satırı döndürür. Şöyle yaparız.
cv::Mat mat = ...;

cv::Vec3b * currentRow;

for (int row = 0; rows < submat.rows; ++rows)
{
  currentRow = submat.ptr<cv::Vec3b>(rows);
  for (int cols = 0; cols < submat.cols; ++cols)
  {
    currentRow[cols][0] = 0;
    currentRow[cols][1] = 0;
  }
}
release metodu
Şöyle yaparız.
image.release();
reshape metodu - channel
Bu metod yeni bir nesne döner. Dolayısıyla şu kod yanlıştır.
cv::Mat colorMat;
colorMat.reshape (1);
Şöyle yapmak gerekir.
colorMat = colorMat.reshape (1);
Resmi tek kanallı hale getirmek için şöyle yaparız. Çok kanallı resmin nasıl tek kanallı hale geldiğini anlatan bir yazı şöyle.
cv::Mat inputImage = cv::imread("testImage.png");
cv::Mat fImage; 

inputImage.convertTo(fImage, CV_64F); 
fImage = fImage.reshape(1); // You need to reshape to single channel
std::vector<double> actualImage(fImage.begin<double>(), fImage.end<double>());
reshape metodu - channel + size
İmzası şöyle
Mat Mat::reshape(int cn, int rows=0) const
Açıklaması şöyle
cn  New number of channels. If the parameter is 0, the number of channels remains
 the same.
rows  New number of rows. If the parameter is 0, the number of rows remains the
 same.
Şöyle yaparız.
#define WIDTH 2048
#define HEIGHT 2048
...

Mat img = Mat(HEIGHT, WIDTH, CV_32FC3);

Mat img_linear = orig_img.reshape(1, HEIGHT*WIDTH);
Şöyle yaparız.
Mat3b img = ...;
int n = img.rows * img.cols;
Mat data = img.reshape(1, n);
rows Alanı
rows ve cols ile dolaşırız. Daha sonra at() metodu ile pixele erişiriz.
for(int j=0; j<image.rows; ++j)
  for(int i=0; i<input.cols; ++i)
  {
    image.at<unsigned char>(j,i) = i/2;
  }
setTo metodu
Kesişen noktaları siyah yapmak için şöyle yaparız.
Mat dst = ...
Mat edges = ...
dst.setTo(0,edges);
size metodu
Şöyle alınır.
Mat destination = ...;
Size s = destination.size();
for (int i = 0; i < s.height; i++)
{
  for (int j = 0; j < s.width; j++) {
            ...;

  }
}
Şöyle alınır ama CvSize artık kullanılmayan bir sınıf.
Mat srcColor = ...
CvSize size = srcColor.size();
t metodu
transpose işlemi yapar. Metodun için şöyledir.
MatExpr MatExpr::t() const
{
  MatExpr e;
  op->transpose(*this, e);
  return e;
}
Şöyle yaparız.
cv::Mat a;
a = a.t();
Eğer inplace çalışmak istersek, yeni bir matrix nesnesi oluşturmamak için aynı şeyi şöyle de yapabiliriz.
cv::Mat a;
cv::transpose(a,a);
total metodu
Şöyle yaparız.
cv::Mat image = ...;
int image_size = image.total() * image.elemSize();
type metodu
Şöyle yaparız.
cv::Mat data = (cv::Mat_<float>(2, 4) << 1.f, 2.f, 3.f, 4.f,
                                         5.f, 6.f, 7.f, 8.f);
cv::Mat res(data.size(), data.type());
Şöyle yaparız.
if(image.type() != CV_8UC3){...}
Şöyle yaparız.
if (image.type() != CV_8UC1) {...}
Type alanını yazdırırsak bir sayı görürüz.
std::cout << "Initial type= " << data.type() << "\n";
Çıktı olarak şunu alırız.
Initial type= 3
u Alanı
Şu tiptendir.
UMatData * u
Şöyle yaparız.
img.u->refcount
vektöre çevirmek
Şöyle yaparız
// 1. Convert vector to Mat

cv::Mat amat(7309, 7697, CV_8UC1, &a[0]);

// 2. Apply 5x5 Gaussian filter

cv::Mat bmat;  // blurred output, sigma=1.4 assumed below
cv::GaussianBlur(amat, bmat, cv::Size(5,5), 1.4); 

// 3. Convert Mat to vector

cv::Mat cmat = bmat.reshape(1, 1); // make the Mat one big long row
std::vector<unsigned char>b = cmat;
vector yerine byte buffer'a çevirmek için şöyle yaparız. Bu yöntem çok doğru olmayabilir.
void MatToBytes(cv::Mat image, uchar ** pimage_uchar)
{
  uchar * image_uchar = * pimage_uchar;
  
  int image_size = image.total() * image.elemSize();
  image_uchar = new uchar[image_size];
  std::memcpy(image_uchar, image.data, image_size * sizeof(uchar));
  
}
operator* metodu
Şöyle yaparız.
Mat mat2 = mat * 10
Aynı şeyi şöyle de yapabiliriz.
// Assuming the image to be RGB 
cv::multiply(frame, cv::Scalar(10, 10, 10), frame, 1, CV_8UC3);
operator / metodu
Şöyle yaparız.
Mat mat2 = mat * 10
Aynı şeyi şöyle de yapabiliriz.
// Assuming the image to be RGB 
cv::divide(frame, cv::Scalar(10, 10, 10), frame, 1, CV_8UC3);
operator== metodu
Açıklaması şöyle
 The result of comparison is an 8-bit single channel mask whose elements are set to 255 (if the particular element or pair of elements satisfy the condition) or 0.
Şöyle yaparız.
cv::Mat mat = (image1 == image2);
operator << - stream'e yazmak
Şöyle yaparız.
cv::Mat res;
...
std::cout << res;
Şöyle bir nesnemiz olsun.
cv::Mat data = cv::Mat::ones(2, 2, CV_16SC1);
std::cout << "Initial data=" << data << "\n";
Çıktı olarak şunu alırız.
Initial data=[1, 1;
  1, 1]
zeros metodu - size + type
Örnek 1
Tek kanallı boş siyah resim oluşturmak için şöyle yaparız.
cv::Mat image = cv::Mat::zeros(cv::Size(200,200), CV_8U); //create zero image
Örnek 2
Tek kanallı boş siyah resim oluşturmak için şöyle yaparız.
cv::Mat image = cv::Mat::zeros(cv::Size(3, 1), CV_64F),
Örnek 3
Bir başka resimle aynı boyutta ve kanalda siyah resim oluşturmak için şöyle yaparız
//First load your source image, here load as gray scale
cv::Mat srcImage = cv::imread("sourceImage.jpg", CV_LOAD_IMAGE_GRAYSCALE);

//Then define your mask image
cv::Mat mask = cv::Mat::zeros(srcImage.size(), srcImage.type());
Diğer
base64 string'e çevirmek için şöyle yaparız.
string mat2str(const Mat& m)
{
  Mat src;
  if (!m.isContinuous()) {
    src = m.clone();
  }
  else {
    src = m;
  }

  // Create header
  int type = m.type();
  int channels = m.channels();
  vector<uchar> data(4*sizeof(int));
  memcpy(&data[0 * sizeof(int)], (uchar*)&m.rows, sizeof(int));
  memcpy(&data[1 * sizeof(int)], (uchar*)&m.cols, sizeof(int));
  memcpy(&data[2 * sizeof(int)], (uchar*)&type, sizeof(int));
  memcpy(&data[3 * sizeof(int)], (uchar*)&channels, sizeof(int));

  // Add image data
  data.insert(data.end(), m.datastart, m.dataend);

  // Encode
  return base64_encode(data.data(), data.size());
}

Geri çevirmek için şöyle yaparız.
Mat str2mat(const string& s)
{
  // Decode data
  string data = base64_decode(s);

  // Decode Header
  int rows;
  int cols;
  int type;
  int channels;
  memcpy((char*)&rows, &data[0 * sizeof(int)], sizeof(int));
  memcpy((char*)&cols, &data[1 * sizeof(int)], sizeof(int));
  memcpy((char*)&type, &data[2 * sizeof(int)], sizeof(int));
  memcpy((char*)&channels, &data[3 * sizeof(int)], sizeof(int));

  // Make the mat
  return Mat(rows, cols, type, (uchar*)&data[4*sizeof(int)]).clone();
}



Hiç yorum yok:

Yorum Gönder