I have 2 images with transparency. Images have the same format and size.
How can I copy pixels from second image to the first one by using C++ OpenCV?
The idea is to draw 2nd image on the 1st image.
Thanks
code from the link in comment above (modified for my case)
L. Scott Johnson thanks you again!
void alphaBlend(Mat& foreground, Mat& background, Mat& alpha, Mat& outImage)
{
// Find number of pixels.
int numberOfPixels = foreground.rows * foreground.cols * foreground.channels();
// Get floating point pointers to the data matrices
float* fptr = reinterpret_cast<float*>(foreground.data);
float* bptr = reinterpret_cast<float*>(background.data);
float* aptr = reinterpret_cast<float*>(alpha.data);
float* outImagePtr = reinterpret_cast<float*>(outImage.data);
// Loop over all pixesl ONCE
for (
int i = 0;
i < numberOfPixels;
i++, outImagePtr++, fptr++/*, aptr++*/, bptr++
)
{
if (i!= 0 && (i % 3) == 0)
aptr++;
*outImagePtr = (*fptr) * (*aptr) + (*bptr) * (1 - *aptr);
}
}
void Mix()
{
Mat layer = imread("images\\leyer.png", IMREAD_UNCHANGED);
Mat image = imread("images\\bg.jpg");
std::vector<cv::Mat> bgra_planes;
cv::split(layer, bgra_planes);
Mat alpha = bgra_planes[3];
bgra_planes.pop_back();
cv::merge(bgra_planes, layer);
alpha.convertTo(alpha, CV_32FC3, 1.0 / 255);
layer.convertTo(layer, CV_32FC3);
image.convertTo(image, CV_32FC3);
Mat result(layer.size(), CV_32FC3);
alphaBlend(layer, image, alpha, result);
result.convertTo(result, CV_8UC3);
// previous tries
//cv::copyTo(layer, image, );
//cv::addWeighted(image, 1, layer, 1, 0.5, result);
String windowName = "alpha blending";
namedWindow(windowName, WINDOW_NORMAL);
imshow(windowName, result);
waitKey(0);
destroyWindow(windowName);
}
Here's what you can try:
load your first image
cv::Mat img = cv::imread("img.jpeg");
find your smaller image - here I'm just resizing the same image
cv::Mat img_resize;
cv::resize(img, img_resize, cv::Size(), 0.3, 0.3);
choose the xy origin location
const cv::Point origin(100, 100);
create a Region of Interest
cv::Rect roi(origin, img_resize.size());
copy the matrix data in
img_resize.copyTo(img(roi));
Related
Recently, I'm interested in image processing using OpenCV, but I'm new to it.
I do some simple image processing on a lot of images, and finally I want to watermark each image with a logo which is a small png image.
There are a lot of codes which blend two images. Here is an example which I used to blend two images:
int main( int argc, char** argv )
{
double alpha = 0.5; double beta; double input;
Mat src1, src2, dst;
// main image with real size.(Large)
src1 = imread("a.jpg");
// logo which will be used as a watermark.(small size)
src2 = imread("logo.png");
namedWindow("Linear Blend", 1);
beta = ( 1.0 - alpha );
addWeighted( src1, alpha, src2, beta, 0.0, dst);
imshow( "Linear Blend", dst );
waitKey(0);
return 0;
}
Here, both images should be the same type and the same size, while my logo image is a small image which I want to blend to the main image in a corner (actually at an arbitrary point).
Can anyone help me to do that? (Maybe, one solution is to create a matrix from the logo which is the same size of the main image so every point outside of the logo should be zero and then finally blend two images which have equal size.)
my final code is like this:
int main( int argc, char** argv )
{
double alpha = 0.5; double beta; double input;
Mat src1, src2, src2_copy, dst;
src1 = imread("a.jpg");
src2 = imread("logo.png");
resize(src2, src2_copy, src2.size() / 2, 0.5, 0.5);
int x = 100;
int y = 100;
int w = src2_copy.size().width;
int h = src2_copy.size().height;
cv::Rect pos = cv::Rect(x, y, w, h);
dst = src1.clone();
namedWindow("Linear Blend", 1);
beta = ( 1.0 - alpha );
addWeighted(src1(pos), alpha, src2_copy, beta, 0.0, dst);
imshow("Linear ", dst);
waitKey(0);
return 0;
}
You can access a (rectangular) region of interest (ROI) inside a cv::Mat using a cv::Rect (see the documentation on the base class), which is described by x, y, width, and height. This is a widely used technique, which becomes handy in a lot of use cases!
So, now you just need to set up a proper ROI within your main image and blend your watermark there. Let's have a look at the following code snippet:
// Artificial main image
cv::Mat img = cv::Mat(300, 300, CV_8UC3, cv::Scalar(128, 128, 128));
// Artificial watermark
cv::Mat wtm = cv::Mat(25, 25, CV_8UC3, cv::Scalar(0, 0, 255));
// Position of watermark in main image
int x = 30;
int y = 35;
int w = wtm.size().width;
int h = wtm.size().height;
cv::Rect pos = cv::Rect(x, y, w, h);
// Blending
double alpha = 0.7;
double beta = (1.0 - alpha);
cv::addWeighted(img(pos), alpha, wtm, beta, 0.0, img(pos));
The artifical main image looks like this:
The artificial watermark image looks like this:
And, the final result looks like this:
As you can see, in
cv::addWeighted(img(pos), alpha, wtm, beta, 0.0, img(pos))
the ROI img(pos) is used as source and destination of the operation, so you have in-place blending. If you want to have a separate output image while preserving your main image untouched, maybe clone your main image in the beginning, i.e.
cv::Mat dst = img.clone()
and then do the blending with dst(pos) instead of img(pos).
Hope that helps!
I was looking at this tutorial, and it said "You can make a symmetric face, by averaging a face and its mirror reflection." - and there was an example of Obama's face being made symmetrical. I tried doing the same with openCV and C++, but these are the results I'm getting using the following code:
Mat3b getMean(const vector<Mat3b>& images) {
Mat m(images[0].rows, images[0].cols, CV_64FC3); // Create a 0 initialized image to use as accumulator
m.setTo(Scalar(0, 0, 0, 0)); //set all image elements to 0
Mat temp; // Use a temp image to hold the conversion of each input image to CV_64FC3
for (int i = 0; i < images.size(); ++i) { //loop through the images
images[i].convertTo(temp, CV_64FC3); // Convert the input images to CV_64FC3...
m += temp; //...so you can accumulate
}
m.convertTo(m, CV_8U, 1. / images.size()); // Convert back to CV_8UC3 type, applying the division to get the actual mean
return m;
}
int main() {
Mat img1 = imread("E:/barack-obama.jpg"), img2, img4;
resize(img1, img1, Size(0.4 * img1.cols, 0.4 * img1.rows), 1, 1, INTER_LINEAR);
flip(img1, img2, +1);
vector<Mat3b> imgs;
imgs.push_back(img1);
imgs.push_back(img2);
Mat3b img3 = getMean(imgs); // Compute the mean
//img3 = (img1 + img2)*0.5;
double alpha = 0.5, beta;
beta = (1.0 - alpha);
addWeighted(img1, alpha, img2, beta, 0.0, img4);
imshow("Original", img1);
imshow("getMean", img3);
imshow("AddWeighted", img4);
waitKey(0);
}
Nvidia's cuDNN for deep learning has a rather interesting format for images called CHW. I have a cv::Mat img; that I want to convert to a one-dimensional vector of floats. The problem that I'm having is that the format of the 1D vector for CHW is (RR...R, GG..G,BB..B).
So I'm curious as to how I can extract the channel values for each pixel and order them for this format.
I faced with same problem and and solve it in that way:
#include <opencv2/opencv.hpp>
cv::Mat hwc2chw(const cv::Mat &image){
std::vector<cv::Mat> rgb_images;
cv::split(image, rgb_images);
// Stretch one-channel images to vector
cv::Mat m_flat_r = rgb_images[0].reshape(1,1);
cv::Mat m_flat_g = rgb_images[1].reshape(1,1);
cv::Mat m_flat_b = rgb_images[2].reshape(1,1);
// Now we can rearrange channels if need
cv::Mat matArray[] = { m_flat_r, m_flat_g, m_flat_b};
cv::Mat flat_image;
// Concatenate three vectors to one
cv::hconcat( matArray, 3, flat_image );
return flat_image;
}
P.S. If input image isn't in RGB format, you can change channel order in matArray creation line.
Use cv::dnn::blobFromImage:
cv::Mat bgr_image = cv::imread(imageFileName);
cv::Mat chw_image = cv::dnn::blobFromImage
(
bgr_image,
1.0, // scale factor
cv::Size(), // spatial size for output image
cv::Scalar(), // mean
true, // swapRB: BGR to RGB
false, // crop
CV_32F // Depth of output blob. Choose CV_32F or CV_8U.
);
const float* data = reinterpret_cast<const float*>(chw_image.data);
int data_length = 1 * 3 * bgr_image.rows * bgr_image.cols;
You can either iterate over the image manually and copy the values into the right place, or you can use something like cv::extractChannel to copy the channels one by one like so:
#include <opencv2/opencv.hpp>
int main()
{
//create dummy 3 channel float image
cv::Mat sourceRGB(cv::Size(100,100),CV_32FC3);
auto size = sourceRGB.size();
for (int y = 0; y < size.height; ++y)
{
for (int x = 0; x < size.width; ++x)
{
float* pxl = sourceRGB.ptr<float>(x, y);
*pxl = x / 100.0f;
*(pxl+1) = y / 100.0f;
*(pxl + 2) = (y / 100.0f) * (x / 100.0f);
}
}
cv::imshow("test", sourceRGB);
cv::waitKey(0);
//create single image with all 3 channels one after the other
cv::Size newsize(size.width,size.height*3);
cv::Mat destination(newsize,CV_32FC1);
//copy the channels from the source image to the destination
for (int i = 0; i < sourceRGB.channels(); ++i)
{
cv::extractChannel(
sourceRGB,
cv::Mat(
size.height,
size.width,
CV_32FC1,
&(destination.at<float>(size.height*size.width*i))),
i);
}
cv::imshow("test", destination);
cv::waitKey(0);
return 0;
}
I have image as follows:
I want to detect 5 dials for processing. Hough circles is detecting all other irrelevant circles. to solve this i created a plain image and generated absolute difference with this one. It gave this image:
I drew box around it and final image is:
My code is as follows:
Mat img1 = imread(image_path1, COLOR_BGR2GRAY);
Mat img2 = imread(image_path2, COLOR_BGR2GRAY);
cv::Mat diffImage;
cv::absdiff(img2, img1, diffImage);
cv::Mat foregroundMask = cv::Mat::zeros(diffImage.rows, diffImage.cols, CV_8UC3);
float threshold = 30.0f;
float dist;
for(int j=0; j<diffImage.rows; ++j)
{
for(int i=0; i<diffImage.cols; ++i)
{
cv::Vec3b pix = diffImage.at<cv::Vec3b>(j,i);
dist = (pix[0]*pix[0] + pix[1]*pix[1] + pix[2]*pix[2]);
dist = sqrt(dist);
if(dist>threshold)
{
foregroundMask.at<unsigned char>(j,i) = 255;
}
}
}
cvtColor(diffImage,diffImage,COLOR_BGR2GRAY);
Mat1b img = diffImage.clone();
// Binarize image
Mat1b bin = img > 70;
// Find non-black points
vector<Point> points;
findNonZero(bin, points);
// Get bounding rect
Rect box = boundingRect(points);
// Draw (in color)
rectangle(img1, box, Scalar(0,255,0), 3);
// Show
imshow("Result", img1);
Now the issue is i cant compare plain image with anyother iamge of different sizes. Any pointer to right direction will be very helpful.
Regards,
Saghir A. Khatr
Edit
My plain image is as follows
I want to create a standard sample plain image which can be used with any image to detect that portion...
I have split an image into 3 separate color channels - one blue, one green, and one red. I would like to normalize each of these channels by the image's intensity, where intensity = (red + blue + green)/3. To be clear, I am trying to make an image that is composed of one of the three color channels, divided by the image's intensity, where the intensity is described by the equation above.
I am new to OpenCV and I do not think I am doing this correctly; when the images are displayed, all the pixels appear to be black.
I am new to OpenCV (I have worked through the tutorials that come with the documentation, but that is it) - any advice about how to go about this normalization would be extremely helpful.
Thanks!
Here is my attempt:
int main(int argc, char** argv){
Mat sourceImage, I;
const char* redWindow = "Red Color Channel";
const char* greenWindow = "Green Color Channel";
const char* blueWindow = "Blue Color Channel";
if(argc != 2)
{
cout << "Incorrect number of arguments" << endl;
}
/* Load the image */
sourceImage = imread(argv[1], 1);
if(!sourceImage.data)
{
cout << "Image failed to load" << endl;
}
/* First, we have to allocate the new channels */
Mat r(sourceImage.rows, sourceImage.cols, CV_8UC1);
Mat b(sourceImage.rows, sourceImage.cols, CV_8UC1);
Mat g(sourceImage.rows, sourceImage.cols, CV_8UC1);
/* Now we put these into a matrix */
Mat out[] = {b, g, r};
/* Split the image into the three color channels */
split(sourceImage, out);
/* I = (r + b + g)/3 */
add(b, g, I);
add(I, r, I);
I = I/3;
Mat red = r/I;
Mat blue = b/I;
Mat green = g/I;
/* Create the windows */
namedWindow(blueWindow, 0);
namedWindow(greenWindow, 0);
namedWindow(redWindow, 0);
/* Show the images */
imshow(blueWindow, blue);
imshow(greenWindow, green);
imshow(redWindow, red);
waitKey(0);
return 0;
}
Once you divide by the intensity the pixel values will be in the range [0, 1], except since they are integers they will be 0 or 1. For a display image white is 255 and 0 is black, so this is why everything appears black to you.
You need to use floating point to get an accurate result, and you need to scale the result by 255 to see it.
Doing that results in this (which I an not sure is particularly useful)
(Image source: BSDS500)
And here is the code that generated it:
#include <opencv2/core/core.hpp>
#include <vector>
int main(int argc, char** argv)
{
// READ RGB color image and convert it to Lab
cv::Mat bgr_image = cv::imread("208001.jpg"); // BSDS500 mushroom
cv::imshow("original image", bgr_image);
cv::Mat bgr_image_f;
bgr_image.convertTo(bgr_image_f, CV_32FC3);
// Extract the color planes and calculate I = (r + g + b) / 3
std::vector<cv::Mat> planes(3);
cv::split(bgr_image_f, planes);
cv::Mat intensity_f((planes[0] + planes[1] + planes[2]) / 3.0f);
cv::Mat intensity;
intensity_f.convertTo(intensity, CV_8UC1);
cv::imshow("intensity", intensity);
//void divide(InputArray src1, InputArray src2, OutputArray dst, double scale=1, int dtype=-1)
cv::Mat b_normalized_f;
cv::divide(planes[0], intensity_f, b_normalized_f);
cv::Mat b_normalized;
b_normalized_f.convertTo(b_normalized, CV_8UC1, 255.0);
cv::imshow("b_normalized", b_normalized);
cv::Mat g_normalized_f;
cv::divide(planes[1], intensity_f, g_normalized_f);
cv::Mat g_normalized;
g_normalized_f.convertTo(g_normalized, CV_8UC1, 255.0);
cv::imshow("g_normalized", g_normalized);
cv::Mat r_normalized_f;
cv::divide(planes[2], intensity_f, r_normalized_f);
cv::Mat r_normalized;
r_normalized_f.convertTo(r_normalized, CV_8UC1, 255.0);
cv::imshow("r_normalized", r_normalized);
cv::waitKey();
}