Editing a matrix in OpenCV? - opencv

I am trying to obtain the first image, make some modification pixel by pixel and write the resultant to a second image.
int main(int argc, char* argv[])
{
namedWindow("Frame");
testVid("D:\\Penguins.mp4");
destroyAllWindows();
return EXIT_SUCCESS;
}
void testVid(char* videoFilename)
{
VideoCapture capture(videoFilename);
Vec3b pixel;
if (!capture.read(frame))
return;
Mat finalframe(frame.rows,frame.cols, CV_8UC3, Scalar(0, 0, 0));
for (int i = 0; i < frame.rows; i++)
for (int j = 0; j < frame.cols; j++)
{
pixel = frame.at<Vec3b>(Point(j, i));
finalframe.at<Vec3b>(Point(j, i)) = pixel;
}
imshow("Frame", finalframe);
keyboard = waitKey(30);
capture.release();
getchar();
}
However, when I display second image using imshow() , A blank grey image is shown.
Now even if I do not choose to do any modifications, I face similar problems when I tried second = first;
The only thing that seems to work is first.copyTo(second);
Any reason why the second image refuses to do anything ??

Related

Get a row from an image

I have an image and I want to get a first row (and then the second, and so on...)
I wrote this code, but it doesn't work as expected:
int main(int argc, char** argv)
{
Mat img = imread("a.jpg");
Mat line, ROI;
for (int i = 0; i<img.rows; i++)
{
for (int i = 0; i<img.cols; i++)
{
ROI = img.clone();
// ROI=img(cropRect);
Mat line = ROI(Rect(0, i, ROI.cols, 1)).clone();
}
}
imshow("line", line);
int k = waitKey(0);
return 0;
}
You can use row to create a matrix header for the specified matrix row. If you need a deep copy, you can then use clone.
Also, you need imshow and waitKey to be inside the loop, or you'll see only the last row.
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("path_to_image");
Mat line;
for (int i = 0; i < img.rows; i++)
{
line = img.row(i);
// Or, for a deep copy:
//line = img.row(i).clone();
imshow("line", line);
waitKey(0);
}
return 0;
}

using DCT for embeding watermark

I wrote down an openCV code .I tried to embed a 64X64 pix watermark image in a 512X512 image.
my code has 5 parts:
reading two pictures( watermark and original image that I want to
embed watermark in it)
resize 2 readed images to specified size.(64X64 for watermark image
and 512X512 for original image)
devide original resized image to 8X8 blocks and transform them with
DCT.
embedding each pixel of watermark in each block of original image.
applying inverse DCT on each block.
I have this problem that all of three imshows have same results.
thank you for your help :)
here is my code :
int _tmain(int argc, _TCHAR* argv[])
{
int index=0;
int iindex=0;
vector<Mat> blocks(4096);
/////////////Part1:reading images
Mat originalImage;
originalImage = imread("C:\\MGC.jpg",CV_LOAD_IMAGE_GRAYSCALE);
Mat watermarkImage;
watermarkImage = imread("C:\\ivp_lg.bmp" , CV_LOAD_IMAGE_GRAYSCALE);
/// show original image
namedWindow("Original");
int x = 0; int y = 0;
moveWindow("Original", x, y);
imshow("Original", originalImage);
x += 100; y += 100;
//////Part 2:Leave originals alone, work on a copys. resize readed images
Mat dctImage = originalImage.clone();
Mat wmrk = watermarkImage.clone();
Mat tmp1(512, 512, CV_8UC1);
Mat tmp2(64, 64, CV_8UC1);
resize(dctImage, dctImage, tmp1.size());
resize(wmrk, wmrk , tmp2.size());
/////Part 3:break dctImage into 8X8 blocks and applying DCT on each block
for (int i = 0; i < 512; i += 8)
{
for (int j = 0; j < 512; j+= 8)
{
Mat block = dctImage(Rect(i, j, 8, 8));
block.convertTo(block, CV_32FC1);
dct(block,blocks[index]);
blocks[index].convertTo(blocks[index], CV_8UC1);
index++;
}
}
/// show transformed image
namedWindow("TransformedImage");
moveWindow("TransformedImage", x, y);
imshow("TransformedImage",dctImage );
x += 100; y += 100;
//////Part 4: embeding watermark. if corresponding pixel of watermark was 255 then element (5,5) in the block increase 200 otherwise do nothing
for(int idx=0 ; idx<4096 ; idx++)
{
int i=idx/64;
int j=idx%64;
float elem=(float) wmrk.at<uchar>(i,j);
if (elem>=128)
{
float tmp=(float) blocks[idx].at<uchar>(5,5);
float temp=tmp +200;
uchar ch=(uchar) temp;
blocks[idx].at<uchar>(5,5)=ch;
}
}
//////Part 5:applying iDCT on each block
for (int i = 0; i < 512; i += 8)
{
for (int j = 0; j < 512; j+= 8)
{
Mat block = dctImage(Rect(i, j, 8, 8));
block.convertTo(block, CV_32FC1);
idct(block,blocks[iindex]);
blocks[iindex].convertTo(blocks[iindex], CV_8UC1);
iindex++;
}
}
/// show watermarked image
namedWindow("WatermarkedImage");
moveWindow("WatermarkedImage", x, y);
imshow("WatermarkedImage",dctImage );
cvWaitKey(80000);
destroyAllWindows();
return 0;
}
#N_Kh As far as I have seen ur code in hurry, You are executing IMSHOW Command over the Matrix dctImage while you are performing operation on different Matrix and vector Block and Blocks respectively.

EM clustering based background foreground segmentation in OPENCV

I tried to perform EM based back ground foreground segmentation using a code below...which I also found in Stackoverflow....But seems there is some error somewhere as I dont ever see the second printf statement to get executed... . basically it is never reaching the classification/clustering part of the code..The code is given below..Could someone help me on this ?
#include <opencv2/opencv.hpp>
#include <opencv2/legacy/legacy.hpp>
char str1[60];
int main()
{
cv::Mat source = cv::imread("C:\\Image Input\\part1.bmp" );
if(!source.data)
printf(" No data \n");
//ouput images
cv::Mat meanImg(source.rows, source.cols, CV_32FC3);
cv::Mat fgImg(source.rows, source.cols, CV_8UC3);
cv::Mat bgImg(source.rows, source.cols, CV_8UC3);
//convert the input image to float
cv::Mat floatSource;
source.convertTo(floatSource, CV_32F);
//now convert the float image to column vector
cv::Mat samples(source.rows * source.cols, 3, CV_32FC1);
int idx = 0;
for (int y = 0; y < source.rows; y++) {
cv::Vec3f* row = floatSource.ptr<cv::Vec3f > (y);
for (int x = 0; x < source.cols; x++) {
samples.at<cv::Vec3f > (idx++, 0) = row[x];
}
}
printf(" After Loop \n");
//we need just 2 clusters
cv::EMParams params(2);
cv::ExpectationMaximization em(samples, cv::Mat(), params);
//the two dominating colors
cv::Mat means = em.getMeans();
//the weights of the two dominant colors
cv::Mat weights = em.getWeights();
//we define the foreground as the dominant color with the largest weight
const int fgId = weights.at<float>(0) > weights.at<float>(1) ? 0 : 1;
printf(" After Training \n");
//now classify each of the source pixels
idx = 0;
for (int y = 0; y < source.rows; y++)
{
printf(" Now Classify\n");
for (int x = 0; x < source.cols; x++)
{
//classify
const int result = cvRound(em.predict(samples.row(idx++), NULL));
//get the according mean (dominant color)
const double* ps = means.ptr<double>(result, 0);
//set the according mean value to the mean image
float* pd = meanImg.ptr<float>(y, x);
//float images need to be in [0..1] range
pd[0] = ps[0] / 255.0;
pd[1] = ps[1] / 255.0;
pd[2] = ps[2] / 255.0;
//set either foreground or background
if (result == fgId) {
fgImg.at<cv::Point3_<uchar> >(y, x, 0) = source.at<cv::Point3_<uchar> >(y, x, 0);
} else {
bgImg.at<cv::Point3_<uchar> >(y, x, 0) = source.at<cv::Point3_<uchar> >(y, x, 0);
}
}
}
printf(" Show Images \n");
cv::imshow("Means", meanImg);
cv::imshow("Foreground", fgImg);
cv::imshow("Background", bgImg);
cv::waitKey(0);
return 0;
}
The code works fine. I think that you use too large images, and learning takes too long time. Try process small images.
Just 1 correction, initialize images with zeros:
//ouput images
cv::Mat meanImg=Mat::zeros(source.rows, source.cols, CV_32FC3);
cv::Mat fgImg=Mat::zeros(source.rows, source.cols, CV_8UC3);
cv::Mat bgImg=Mat::zeros(source.rows, source.cols, CV_8UC3);

Algorithm for shrinking/limiting palette of an image

as input data I have a 24 bit RGB image and a palette with 2..20 fixed colours. These colours are in no way spread regularly over the full colour range.
Now I have to modify the colours of input image so that only the colours of the given palette are used - using the colour out of the palette that is closest to the original colour (not closest mathematically but for human's visual impression). So what I need is an algorithm that uses an input colour and finds the colour in target palette that visually fits best to this colour. Please note: I'm not looking for a stupid comparison/difference algorithm but for something that really incorporates the impression a colour has on humans!
Since this is something that already should have been done and because I do not want to re-invent the wheel again: is there some example source code out there that does this job? In best case it is really a piece of code and not a link to a desastrous huge library ;-)
(I'd guess OpenCV does not provide such a function?)
Thanks
You should look at the Lab color space. It was designed so that the distance in the colour space equals the perceptual distance. So once you have converted your image you can compute the distances as you would have done earlier, but should get a better result from a perceptual point of view. In OpenCV you can use the cvtColor(source, destination, CV_BGR2Lab) function.
Another Idea would be to use dithering. The idea is to mix missing colours using neighbouring pixels. A popular algorithm for this is Floyd-Steinberg dithering.
Here is an example of mine, where I combined a optimized palette using k-means with the Lab colourspace and floyd steinberg dithering:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
cv::Mat floydSteinberg(cv::Mat img, cv::Mat palette);
cv::Vec3b findClosestPaletteColor(cv::Vec3b color, cv::Mat palette);
int main(int argc, char** argv)
{
// Number of clusters (colors on result image)
int nrColors = 18;
cv::Mat imgBGR = imread(argv[1],1);
cv::Mat img;
cvtColor(imgBGR, img, CV_BGR2Lab);
cv::Mat colVec = img.reshape(1, img.rows*img.cols); // change to a Nx3 column vector
cv::Mat colVecD;
colVec.convertTo(colVecD, CV_32FC3, 1.0); // convert to floating point
cv::Mat labels, centers;
cv::kmeans(colVecD, nrColors, labels,
cv::TermCriteria(CV_TERMCRIT_ITER, 100, 0.1),
3, cv::KMEANS_PP_CENTERS, centers); // compute k mean centers
// replace pixels by there corresponding image centers
cv::Mat imgPosterized = img.clone();
for(int i = 0; i < img.rows; i++ )
for(int j = 0; j < img.cols; j++ )
for(int k = 0; k < 3; k++)
imgPosterized.at<Vec3b>(i,j)[k] = centers.at<float>(labels.at<int>(j+img.cols*i),k);
// convert palette back to uchar
cv::Mat palette;
centers.convertTo(palette,CV_8UC3,1.0);
// call floyd steinberg dithering algorithm
cv::Mat fs = floydSteinberg(img, palette);
cv::Mat imgPosterizedBGR, fsBGR;
cvtColor(imgPosterized, imgPosterizedBGR, CV_Lab2BGR);
cvtColor(fs, fsBGR, CV_Lab2BGR);
imshow("input",imgBGR); // original image
imshow("result",imgPosterizedBGR); // posterized image
imshow("fs",fsBGR); // floyd steinberg dithering
waitKey();
return 0;
}
cv::Mat floydSteinberg(cv::Mat imgOrig, cv::Mat palette)
{
cv::Mat img = imgOrig.clone();
cv::Mat resImg = img.clone();
for(int i = 0; i < img.rows; i++ )
for(int j = 0; j < img.cols; j++ )
{
cv::Vec3b newpixel = findClosestPaletteColor(img.at<Vec3b>(i,j), palette);
resImg.at<Vec3b>(i,j) = newpixel;
for(int k=0;k<3;k++)
{
int quant_error = (int)img.at<Vec3b>(i,j)[k] - newpixel[k];
if(i+1<img.rows)
img.at<Vec3b>(i+1,j)[k] = min(255,max(0,(int)img.at<Vec3b>(i+1,j)[k] + (7 * quant_error) / 16));
if(i-1 > 0 && j+1 < img.cols)
img.at<Vec3b>(i-1,j+1)[k] = min(255,max(0,(int)img.at<Vec3b>(i-1,j+1)[k] + (3 * quant_error) / 16));
if(j+1 < img.cols)
img.at<Vec3b>(i,j+1)[k] = min(255,max(0,(int)img.at<Vec3b>(i,j+1)[k] + (5 * quant_error) / 16));
if(i+1 < img.rows && j+1 < img.cols)
img.at<Vec3b>(i+1,j+1)[k] = min(255,max(0,(int)img.at<Vec3b>(i+1,j+1)[k] + (1 * quant_error) / 16));
}
}
return resImg;
}
float vec3bDist(cv::Vec3b a, cv::Vec3b b)
{
return sqrt( pow((float)a[0]-b[0],2) + pow((float)a[1]-b[1],2) + pow((float)a[2]-b[2],2) );
}
cv::Vec3b findClosestPaletteColor(cv::Vec3b color, cv::Mat palette)
{
int i=0;
int minI = 0;
cv::Vec3b diff = color - palette.at<Vec3b>(0);
float minDistance = vec3bDist(color, palette.at<Vec3b>(0));
for (int i=0;i<palette.rows;i++)
{
float distance = vec3bDist(color, palette.at<Vec3b>(i));
if (distance < minDistance)
{
minDistance = distance;
minI = i;
}
}
return palette.at<Vec3b>(minI);
}
Try this algorithm (it will reduct color number, but it compute palette by itself):
#include <opencv2/opencv.hpp>
#include "opencv2/legacy/legacy.hpp"
#include <vector>
#include <list>
#include <iostream>
using namespace cv;
using namespace std;
void main(void)
{
// Number of clusters (colors on result image)
int NrGMMComponents = 32;
// Source file name
string fname="D:\\ImagesForTest\\tools.jpg";
cv::Mat SampleImg = imread(fname,1);
//cv::GaussianBlur(SampleImg,SampleImg,Size(5,5),3);
int SampleImgHeight = SampleImg.rows;
int SampleImgWidth = SampleImg.cols;
// Pick datapoints
vector<Vec3d> ListSamplePoints;
for (int y=0; y<SampleImgHeight; y++)
{
for (int x=0; x<SampleImgWidth; x++)
{
// Get pixel color at that position
Vec3b bgrPixel = SampleImg.at<Vec3b>(y, x);
uchar b = bgrPixel.val[0];
uchar g = bgrPixel.val[1];
uchar r = bgrPixel.val[2];
if(rand()%25==0) // Pick not every, bu t every 25-th
{
ListSamplePoints.push_back(Vec3d(b,g,r));
}
} // for (x)
} // for (y)
// Form training matrix
Mat labels;
int NrSamples = ListSamplePoints.size();
Mat samples( NrSamples, 3, CV_32FC1 );
for (int s=0; s<NrSamples; s++)
{
Vec3d v = ListSamplePoints.at(s);
samples.at<float>(s,0) = (float) v[0];
samples.at<float>(s,1) = (float) v[1];
samples.at<float>(s,2) = (float) v[2];
}
cout << "Learning to represent the sample distributions with" << NrGMMComponents << "gaussians." << endl;
// Algorithm parameters
CvEMParams params;
params.covs = NULL;
params.means = NULL;
params.weights = NULL;
params.probs = NULL;
params.nclusters = NrGMMComponents;
params.cov_mat_type = CvEM::COV_MAT_GENERIC; // DIAGONAL, GENERIC, SPHERICAL
params.start_step = CvEM::START_AUTO_STEP;
params.term_crit.max_iter = 1500;
params.term_crit.epsilon = 0.001;
params.term_crit.type = CV_TERMCRIT_ITER|CV_TERMCRIT_EPS;
//params.term_crit.type = CV_TERMCRIT_ITER;
// Train
cout << "Started GMM training" << endl;
CvEM em_model;
em_model.train( samples, Mat(), params, &labels );
cout << "Finished GMM training" << endl;
// Result image
Mat img = Mat::zeros( Size( SampleImgWidth, SampleImgHeight ), CV_8UC3 );
// Ask classifier for each pixel
Mat sample( 1, 3, CV_32FC1 );
Mat means;
means=em_model.getMeans();
for(int i = 0; i < img.rows; i++ )
{
for(int j = 0; j < img.cols; j++ )
{
Vec3b v=SampleImg.at<Vec3b>(i,j);
sample.at<float>(0,0) = (float) v[0];
sample.at<float>(0,1) = (float) v[1];
sample.at<float>(0,2) = (float) v[2];
int response = cvRound(em_model.predict( sample ));
img.at<Vec3b>(i,j)[0]=means.at<double>(response,0);
img.at<Vec3b>(i,j)[1]=means.at<double>(response,1);
img.at<Vec3b>(i,j)[2]=means.at<double>(response,2);
}
}
img.convertTo(img,CV_8UC3);
imshow("result",img);
waitKey();
// Save the result
cv::imwrite("result.png", img);
}
PS: For perceptive color distance measurement it's better to use L*a*b color space. There is converter in opencv for this purpose. For clustering you can use k-means with defined cluster centers (your palette entries). After clustering you'll get points with indexes of palette intries.

Can we use openCV to load and resize 2d image that had indexed colors?

So, I have an image cv::Mat created as an indexed 2D matrix with colors 1,2,3,... up to 255. I want to resize my image all at once but do it like I currently do - individually for each index, so as not to get mixed colors:
//...
std::map<unsigned char , cv::Mat* > clusters;
for(int i = 0; i < sy; ++i)
{
for(int j = 0; j < sx; ++j)
{
unsigned char current_k = image[i][j];
if (clusters[current_k] == NULL) {
clusters[current_k] = new cv::Mat();
(*clusters[current_k]) = cv::Mat::zeros(cv::Size(sx, sy), CV_8UC1);
}
(*clusters[current_k]).row(i).col(j) = 255;
}
}
std::vector<cv::Mat> result;
for( std::map<unsigned char, cv::Mat*>::iterator it = clusters.begin(); it != clusters.end(); ++it )
{
cv::Mat filled(cv::Size(w, h), (*it->second).type());
cv::resize((*it->second), filled, filled.size(), 0,0, CV_INTER_CUBIC);
cv::threshold( filled, filled, 1, 255, CV_THRESH_BINARY);
result.push_back(filled);
}
So, can OpenCV help me with the automation of my indexed image (so that I could not create cv::Mat per each cluster for a correct resize)?
you can use the Remap function with your own mash to interpolate the values as you'de like
take a look at this tutorial (Link)

Resources