I have a RGB image and I trying to do some modification on R channel. So I do similar to the following:
Mat img;
vector<Mat> chs;
//....
split(img, chs);
//some modification on chs[2]
imshow("Result", img);
But it seems that OpenCV copy data to chs by value (not by reference). As a result the img matrix not changed. But due to memory limitations I don't prefer to use merge function.
Is there any alternative to split the matrix in-place?
split will always copy the data, since it's creating new matrices.
The simplest way to work on, say, red channel will be using split and merge:
Mat3b img(10,10,Vec3b(1,2,3));
vector<Mat1b> planes;
split(img, planes);
// Work on red plane
planes[2](2,3) = 5;
merge(planes, img);
Note that merge doesn't allocate any new memory, so if you're ok with split, there isn't any good reason not to call also merge.
You can always work on the R channel directly:
Mat3b img(10,10,Vec3b(1,2,3));
// Work on red channel, [2]
img(2,3)[2] = 5;
If you want to save the memory used by split, you can work directly on the red channel, but it's more cumbersome:
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
Mat3b img(10,10,Vec3b(1,2,3));
// Create a column matrix header with red plane unwound
// No copies here
Mat1b R = img.reshape(1, img.rows*img.cols).colRange(2, 3);
// Work on red plane
int r = 2;
int c = 3;
// You need to access by index, not by (row, col).
// This will also modify img
R(img.rows * r + c) = 5;
return 0;
}
You can probably find a good compromise by copying the red channel only in a new matrix (avoiding to allocate space also for other channels), and then by copying the result back into original image:
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
Mat3b img(10,10,Vec3b(1,2,3));
// Allocate space only for red channel
Mat1b R(img.rows, img.cols);
for (int r=0; r<img.rows; ++r)
for(int c=0; c<img.cols; ++c)
R(r, c) = img(r, c)[2];
// Work on red plane
R(2,3) = 5;
// Copy back into img
for (int r = 0; r<img.rows; ++r)
for (int c = 0; c<img.cols; ++c)
img(r, c)[2] = R(r,c);
return 0;
}
Thanks to #sturkmen for reviewing the answer
Related
Code :
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
// Mat faceImage = imread("/Users/softech66/Downloads/images 2/unvese_space.png");
// Mat glassBGR = imread("/Users/softech66/Downloads/images 2/sun.png");
// /Users/softech66/Downloads/images 2/sun_mask.bmp
// resize(glassBGR,glassBGR,Size(300,100));
// check each pixel of glass and if its white(255,255,255) then change it with face image pixels
Mat img2 = imread("/Users/softech66/Downloads/images 2/sun.png",0);
Mat img4 = imread("/Users/softech66/Downloads/images 2/univese_space.png",0);
Mat img3 = imread("/Users/softech66/Downloads/images 2/sun_mask.bmp",0);
Mat img;
cvtColor(imread("/Users/softech66/Downloads/images 2/sun.png", IMREAD_COLOR), img , cv::COLOR_RGB2RGBA);
Mat mask = imread("/Users/softech66/Downloads/images 2/sun_mask.bmp", IMREAD_GRAYSCALE);
for(int r = 0; r < img.rows; r++){
for(int c = 0; c < img.cols; c++){
uchar alpha = 0;
if(r < mask.rows && c < mask.cols)
alpha = mask.at<uchar>(r, c);
img.at<Vec4b>(r, c)[3] = alpha;
}
}
imwrite("result.png", img);
Mat roi=img4(Rect(0,0,img.cols,img2.rows));
Mat mask1(roi.rows,roi.cols,roi.depth(),Scalar(1));
img.copyTo(roi,mask1);
// img4.copyTo(img);
imshow("img3", img4);
// imshow("img33", img4);
waitKey(0);
}
The images above are come in pairs, as you need to use the corresponding “mask” image to let OpenCV to only copy the circle region into the universe space image. Note for the mask image, the extension name is “.bmp”. This is because the bmp format provide higher precision for the bit storage. But it makes no difference for OpenCV to load them by using “imread(..., 0)” in the same manner with the second parameter “0” to indicate a single channel or grey image to be load.
Moon Mask
Earth Mask
Sun Mask
Universe Space
Moon image
Earth image
Sun image
Result image
I am making a function using C++ and OpenCV that will detect the color of a pixel in an image, determine what color range it is in, and replace it with a generic color. For example, green could range from dark green to light green, the program would determine that its still green and replace it with a simple green, making the output image very simple looking. everything is set up but I'm having trouble defining the characteristics of each range and was curious if anyone knows or a formula that, given BGR values, could determine the overall color of a pixel. If not I'll have to do much experimentation and make it myself, but if something already exists that'd save time. I've done plenty of research and haven't found anything so far.
If you want to make your image simpler (i.e. with less colors), but good looking, you have a few options:
A simple approach would be to divide (integer division) by a factor N the image, and then multiply by a factor N.
Or you can divide your image into K colors, using some clustering algorithm such as kmeans showed here, or median-cut algorithm.
Original image:
Reduced colors (quantized, N = 64):
Reduced colors (clustered, K = 8):
Code Quantization:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
uchar N = 64;
img /= N;
img *= N;
imshow("Reduced", img);
waitKey();
return 0;
}
Code kmeans:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
// Cluster
int K = 8;
int n = img.rows * img.cols;
Mat data = img.reshape(1, n);
data.convertTo(data, CV_32F);
vector<int> labels;
Mat1f colors;
kmeans(data, K, labels, cv::TermCriteria(), 1, cv::KMEANS_PP_CENTERS, colors);
for (int i = 0; i < n; ++i)
{
data.at<float>(i, 0) = colors(labels[i], 0);
data.at<float>(i, 1) = colors(labels[i], 1);
data.at<float>(i, 2) = colors(labels[i], 2);
}
Mat reduced = data.reshape(3, img.rows);
reduced.convertTo(reduced, CV_8U);
imshow("Reduced", reduced);
waitKey();
return 0;
}
Yes, what you probably mean by "Overall color of a pixel" is either the "Hue" or "Saturation" of the color.
So you want a formula that transform RGB to HSV (Hue, Saturation, Value), and then you would only be interested by the Hue or Saturation values.
See: Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
EDIT: You might need to max out the saturation, and then convert it back to RGB, and inspect which value is the highest (for instance (255,0,0), or (255,0,255), etc.
If you want to access RGB value of all pixels , then below is code,
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("image_path");
for(int row = 1; row < image.rows; row++)
{
for(int col = 1; col < image.cols; col++)
{
Vec3b rgb = image.at<Vec3b>(row, col);
}
}
}
I am making a function using C++ and OpenCV that will detect the color of a pixel in an image, determine what color range it is in, and replace it with a generic color. For example, green could range from dark green to light green, the program would determine that its still green and replace it with a simple green, making the output image very simple looking. everything is set up but I'm having trouble defining the characteristics of each range and was curious if anyone knows or a formula that, given BGR values, could determine the overall color of a pixel. If not I'll have to do much experimentation and make it myself, but if something already exists that'd save time. I've done plenty of research and haven't found anything so far.
If you want to make your image simpler (i.e. with less colors), but good looking, you have a few options:
A simple approach would be to divide (integer division) by a factor N the image, and then multiply by a factor N.
Or you can divide your image into K colors, using some clustering algorithm such as kmeans showed here, or median-cut algorithm.
Original image:
Reduced colors (quantized, N = 64):
Reduced colors (clustered, K = 8):
Code Quantization:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
uchar N = 64;
img /= N;
img *= N;
imshow("Reduced", img);
waitKey();
return 0;
}
Code kmeans:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
// Cluster
int K = 8;
int n = img.rows * img.cols;
Mat data = img.reshape(1, n);
data.convertTo(data, CV_32F);
vector<int> labels;
Mat1f colors;
kmeans(data, K, labels, cv::TermCriteria(), 1, cv::KMEANS_PP_CENTERS, colors);
for (int i = 0; i < n; ++i)
{
data.at<float>(i, 0) = colors(labels[i], 0);
data.at<float>(i, 1) = colors(labels[i], 1);
data.at<float>(i, 2) = colors(labels[i], 2);
}
Mat reduced = data.reshape(3, img.rows);
reduced.convertTo(reduced, CV_8U);
imshow("Reduced", reduced);
waitKey();
return 0;
}
Yes, what you probably mean by "Overall color of a pixel" is either the "Hue" or "Saturation" of the color.
So you want a formula that transform RGB to HSV (Hue, Saturation, Value), and then you would only be interested by the Hue or Saturation values.
See: Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
EDIT: You might need to max out the saturation, and then convert it back to RGB, and inspect which value is the highest (for instance (255,0,0), or (255,0,255), etc.
If you want to access RGB value of all pixels , then below is code,
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("image_path");
for(int row = 1; row < image.rows; row++)
{
for(int col = 1; col < image.cols; col++)
{
Vec3b rgb = image.at<Vec3b>(row, col);
}
}
}
I'm trying to get the the difference between two cv::Mat frames in OpenCv. So here is what I tried,
#include<opencv2\opencv.hpp>
#include<opencv2\calib3d\calib3d.hpp>
#include<opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
int main ()
{
cv::VideoCapture cap(0);
cv::Mat frame, frame1,frame2;
int key=0;
while(key!=27){
cap >> frame;
if(key=='c'){
frame1 = frame;
key = 0;
}
if(key =='x'){
cv::absdiff(frame, frame1, frame2); // I also tried frame2= (frame -frame1)*255;
cv::imshow("difference ",frame2);
key =0;
}
cv::imshow("stream",frame);
key = cv::waitKey(10);
}
}
the result is always the same a 0 Matrix, any idea what I'm doing wrong here?
thanks in advance for your help.
Mat objects are pointer typed. After setting frame1 to frame directly using frame1 = frame, both matrices show the same point and same frame also. You have to copy frame value using "copyTo" method of Mat.
OpenCV Matrixes use pointers internally
The documentation of the Mat type states:
Mat is basically a class with two data parts: the matrix header and a pointer to the matrix containing the pixel values.
[...]
Whenever somebody copies a header of a Mat object, a counter is increased for the matrix. Whenever a header is cleaned this counter is decreased. When the counter reaches zero the matrix too is freed. Sometimes you will want to copy the matrix itself too, so OpenCV provides the clone() and copyTo() functions.
cv::Mat F = A.clone();
cv::Mat G;
A.copyTo(G);
OpenCV overloads the affectation operator on cv::Mat objects so that the line mat1 = mat2 only affects the pointer to the data in mat1 (that points to the same data as mat2). This avoids time consuming copies of all the image data.
If you want to save the data of a matrix, you have to write mat1 = mat2.clone() or mat2.copyTo(mat1).
I was looking for a similar program and I came across your post, here is a sample I have written for frameDifferencing, hope this helps, the below function will give you the difference between two frames
/** #function differenceFrame */
Mat differenceFrame( Mat prev_frame, Mat curr_frame )
{
Mat image = prev_frame.clone();
printf("frame rows %d Cols %d\n" , image.rows, image.cols);
for (int rows = 0; rows < image.rows; rows++)
{
for (int cols = 0; cols < image.cols; cols++)
{
/* printf("BGR value %lf %lf %lf\n" , abs(prev_frame.at<cv::Vec3b>(rows,cols)[0] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]),
abs(prev_frame.at<cv::Vec3b>(rows,cols)[1] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]),
abs(prev_frame.at<cv::Vec3b>(rows,cols)[2] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]));
*/
image.at<cv::Vec3b>(rows,cols)[0] = abs(prev_frame.at<cv::Vec3b>(rows,cols)[0] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]);
image.at<cv::Vec3b>(rows,cols)[1] = abs(prev_frame.at<cv::Vec3b>(rows,cols)[1] -
curr_frame.at<cv::Vec3b>(rows,cols)[1]);
image.at<cv::Vec3b>(rows,cols)[2] = abs(prev_frame.at<cv::Vec3b>(rows,cols)[2] -
curr_frame.at<cv::Vec3b>(rows,cols)[2]);
}
}
return image;
}
I'm having problems getting PCA and Eigenfaces working using the latest C++ syntax with the Mat and PCA classes. The older C syntax took an array of IplImage* as a parameter to perform its processing and the current API only takes a Mat that is formatted by Column or Row. I took the Row approach using the reshape function to fit my image's matrix to fit in a single row. I eventually want to take this data and then use the SVM algorithm to perform detection, but when I do that all my data is just a stream of 0s. Can someone please help me out? What am I doing wrong? Thanks!
I saw this question and it's somewhat related, but I'm not sure what the solution is.
This is basically what I have:
vector<Mat> images; //This variable will be loaded with a set of images to perform PCA on.
Mat values(images.size(), 1, CV_32SC1); //Values are the corresponding values to each of my images.
int nEigens = images.size() - 1; //Number of Eigen Vectors.
//Load the images into a Matrix
Mat desc_mat(images.size(), images[0].rows * images[0].cols, CV_32FC1);
for (int i=0; i<images.size(); i++) {
desc_mat.row(i) = images[i].reshape(1, 1);
}
Mat average;
PCA pca(desc_mat, average, CV_PCA_DATA_AS_ROW, nEigens);
Mat data(desc_mat.rows, nEigens, CV_32FC1); //This Mat will contain all the Eigenfaces that will be used later with SVM for detection
//Project the images onto the PCA subspace
for(int i=0; i<images.size(); i++) {
Mat projectedMat(1, nEigens, CV_32FC1);
pca.project(desc_mat.row(i), projectedMat);
data.row(i) = projectedMat.row(0);
}
CvMat d1 = (CvMat)data;
CvMat d2 = (CvMat)values;
CvSVM svm;
svm.train(&d1, &d2);
svm.save("svmdata.xml");
What etarion said is correct.
To copy a column or row you always have to write:
Mat B = mat.col(i);
A.copyTo(B);
The following program shows how to perform a PCA in OpenCV. It'll show the mean image and the first three Eigenfaces. The images I used in there are available from http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html:
#include "cv.h"
#include "highgui.h"
using namespace std;
using namespace cv;
Mat normalize(const Mat& src) {
Mat srcnorm;
normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
return srcnorm;
}
int main(int argc, char *argv[]) {
vector<Mat> db;
// load greyscale images (these are from http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html)
db.push_back(imread("s1/1.pgm",0));
db.push_back(imread("s1/2.pgm",0));
db.push_back(imread("s1/3.pgm",0));
db.push_back(imread("s2/1.pgm",0));
db.push_back(imread("s2/2.pgm",0));
db.push_back(imread("s2/3.pgm",0));
db.push_back(imread("s3/1.pgm",0));
db.push_back(imread("s3/2.pgm",0));
db.push_back(imread("s3/3.pgm",0));
db.push_back(imread("s4/1.pgm",0));
db.push_back(imread("s4/2.pgm",0));
db.push_back(imread("s4/3.pgm",0));
int total = db[0].rows * db[0].cols;
// build matrix (column)
Mat mat(total, db.size(), CV_32FC1);
for(int i = 0; i < db.size(); i++) {
Mat X = mat.col(i);
db[i].reshape(1, total).col(0).convertTo(X, CV_32FC1, 1/255.);
}
// Change to the number of principal components you want:
int numPrincipalComponents = 12;
// Do the PCA:
PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, numPrincipalComponents);
// Create the Windows:
namedWindow("avg", 1);
namedWindow("pc1", 1);
namedWindow("pc2", 1);
namedWindow("pc3", 1);
// Mean face:
imshow("avg", pca.mean.reshape(1, db[0].rows));
// First three eigenfaces:
imshow("pc1", normalize(pca.eigenvectors.row(0)).reshape(1, db[0].rows));
imshow("pc2", normalize(pca.eigenvectors.row(1)).reshape(1, db[0].rows));
imshow("pc3", normalize(pca.eigenvectors.row(2)).reshape(1, db[0].rows));
// Show the windows:
waitKey(0);
}
and if you want to build the matrix by row (like in your original question above) use this instead:
// build matrix
Mat mat(db.size(), total, CV_32FC1);
for(int i = 0; i < db.size(); i++) {
Mat X = mat.row(i);
db[i].reshape(1, 1).row(0).convertTo(X, CV_32FC1, 1/255.);
}
and set the flag in the PCA to:
CV_PCA_DATA_AS_ROW
Regarding machine learning. I wrote a document on machine learning with the OpenCV C++ API that has examples for most of the classifiers, including Support Vector Machines. Maybe you can get some inspiration there: http://www.bytefish.de/pdf/machinelearning.pdf.
data.row(i) = projectedMat.row(0);
This will not work. operator= is a shallow copy, meaning no data is actually copied. Use
cv::Mat sample = data.row(i); // also a shallow copy, points to old data!
projectedMat.row(0).copyTo(sample);
The same also for:
desc_mat.row(i) = images[i].reshape(1, 1);
I would suggest looking at the newly checked in tests in svn head
modules/core/test/test_mat.cpp
online here : https://code.ros.org/svn/opencv/trunk/opencv/modules/core/test/test_mat.cpp
has examples for PCA in the old c and new c++
Hope that helps!