Which is the most efficient way to do alpha mask in opencv? - performance

I know OpenCV only supports binary masks.
But I need to do an overlay where I have a grayscale mask that specifies transparency of the overlay.
Eg. if a pixel in the mask is 50% white it should mean a cv::addWeighted operation for that pixel with alpha=beta=0.5, gamma = 0.0.
Now, if there is no opencv library function, what algorithm would you suggest as the most efficient?

I did something like this for a fix.
typedef double Mask_value_t;
typedef Mat_<Mask_value_t> Mask;
void cv::addMasked(const Mat& src1, const Mat& src2, const Mask& mask, Mat& dst)
{
MatConstIterator_<Vec3b> it1 = src1.begin<Vec3b>(), it1_end = src1.end<Vec3b>();
MatConstIterator_<Vec3b> it2 = src2.begin<Vec3b>();
MatConstIterator_<Mask_value_t> mask_it = mask.begin();
MatIterator_<Vec3b> dst_it = dst.begin<Vec3b>();
for(; it1 != it1_end; ++it1, ++it2, ++mask_it, ++dst_it)
*dst_it = (*it1) * (1.0-*mask_it) + (*it2) * (*mask_it);
}
I have not optimized nor made safe this code yet with assertions.
Working assumptions: all Mat's and the Mask are the same size and Mat's are normal three channel color images.

I have a similar problem, where I wanted to apply a png with transparency.
My solution was using Mat expressions:
void AlphaBlend(const Mat& imgFore, Mat& imgDst, const Mat& alpha)
{
vector<Mat> vAlpha;
Mat imgAlpha3;
for(int i = 0; i < 3; i++) vAlpha.push_back(alpha);
merge(vAlpha,imgAlpha3)
Mat blend = imgFore.mul(imgAlpha3,1.0/255) +
imgDst.mul(Scalar::all(255)-imgAlpha3,1.0/255);
blend.copyTo(imgDst);
}

OpenCV supports RGBA images which you can create by using mixchannels or the split and merge functions to combine your images with your greyscale mask. I hope this is what you are looking for!
Using this method you can combine your grayscale mask with your image like so:
cv::Mat gray_image, mask, rgba_image;
std::vector<cv::Mat> result;
cv::Mat image = cv::imread(image_path);
cv::split(image, result);
cv::cvtColor(image, gray_image, CV_BGR2GRAY);
cv::threshold(gray_image, mask, 128, 255, CV_THRESH_BINARY);
result.push_back(mask);
cv::merge(result, rgba_image);
imwrite("rgba.png", rgba_image);
Keep in mind that you cannot view RGBA images using cv::imshow as described in read-rgba-image-opencv and you cannot save your image as jpeg since that format does not support transparency. It seems that you can combine channels using cv::cvtcolor as shown in opencv-2-3-convert-mat-to-rgba-pixel-array

Related

How to flatten an image using OpenCV correctly for image processing and then convert it to Mat again?

I have an image, read using "cv::imread". I have to flatten it so that I could use CUDA & GPU for my image processing algorithms acceleration.
My problem: When I read my image, I can show it correctly using imshow, however when I flatten it and convert it to a Mat object to be used with imshow, only part of my image is displayed. The size of the output image is also wrong, meaning that some data is really lost. What's the problem with my for loop?
// The problematic part of my code
// The Camera Man gray test image
const char* img_gray_name = "../../Test_Images/cameraman.tiff";
const char* img_blur_name = "../cameraman-blur.tiff";
const char* image_general_name = "cameraman_blur";
cv::Mat img = cv::imread(img_gray_name);
unsigned long int img_gray_size = img.rows * img.cols * sizeof(uchar);
uchar *h_img_in;// input image, converted to a flat array to be
// processed by GPU
h_img_in = (uchar *)malloc(img_gray_size);
//*************** The bug should be here! ***************//
for (int i = 0; i < img.rows; ++i) {
for (int j = 0; j < img.cols; ++j) {
h_img_in[i*img.cols+j] = img.at<uchar>(i, j);
}
}
Mat img_test;
img_test = Mat(cv::Size(img.cols, img.rows), CV_8U, h_img_in);
imwrite(img_blur_name, img_test);
// create image window named "camera man"
cv::namedWindow(image_general_name);
// show the image on window
cv::imshow(image_general_name, img_test);
P.S.: I also tested with a new 2D array instead of 1D h_img_in, result is the same; This means that something goes wrong with my usage of "img.at(i, j)".

How can I erase the edge of a waterprint?

I have a src pic like that (the source pic is too large to upload):
and I have a white background logo like that :
I tried to use the OpenCV code:
std::string file_name = "E:\\xxx\\IMG_0001.JPG";
cv::Mat image = cv::imread(file_name);
cv::Mat mask_not;
cv::Mat mask = cv::imread("E:\\xxx\\white_eva.jpg",0);
cv::Mat logo = cv::imread("E:\\xxx\\white_eva.jpg");
cv::bitwise_not(mask,mask_not);
cv::cvtColor(mask_not,mask_not,cv::COLOR_GRAY2BGR);
std::cout<<mask_not.type()<<std::endl;
cv::Mat imageROI;
imageROI = image(cv::Rect(image.cols-logo.cols-10,image.rows-logo.rows-10,logo.cols,logo.rows));
cv::imwrite("E:\\xxx\\imageROI.jpg",imageROI);
logo.copyTo(imageROI,mask_not);
cv::imwrite("E:\\xxx\\test.JPG",image);
The result like that:
As you can see from the result pic, there is a white edge around the logo. At first, I think the reason is that the mask doesn't large enough to mask the logo all. But as you can see the edge of the logo seems to show entirely. So, it confused me. The first question is that how can erase the white edge of the waterprint?
I tried adding morphology operations on your mask image. So this is what I could reach.
std::string file_name = "./image1.jpg";
cv::Mat image = cv::imread(file_name);
cv::Mat mask_not;
cv::Mat mask = cv::imread("./eva.jpg",0);
cv::Mat logo = cv::imread("./eva.jpg");
// MORPHOLOGY OPS HERE
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
Size(5, 5),
Point(-1, -1));
for (int i = 0; i < 20; ++i) {
cv::morphologyEx( mask, mask, cv::MORPH_CLOSE, element );
cv::morphologyEx( mask, mask, cv::MORPH_CLOSE, element );
cv::morphologyEx( mask, mask, cv::MORPH_CLOSE, element );
cv::medianBlur(mask, mask, 5);
}
cv::Mat element_dilate = cv::getStructuringElement(cv::MORPH_RECT,
Size(5, 5),
Point(-1, -1));
cv::dilate(mask, mask, element_dilate);
cv::bitwise_not(mask,mask_not);
cv::imshow("win", mask_not);
cv::waitKey(0);
cv::cvtColor(mask_not,mask_not,cv::COLOR_GRAY2BGR);
std::cout<<mask_not.type()<<std::endl;
cv::Mat imageROI;
imageROI = image(cv::Rect(image.cols-logo.cols-10,image.rows-logo.rows-10,logo.cols,logo.rows));
cv::imwrite("./imageROI.jpg",imageROI);
logo.copyTo(imageROI,mask_not);
cv::imwrite("./test.JPG",image);
Result image (I resized images a bit, so you may be need to change kernel sizes in morphology operations):

How to improve intensity contrast in image?

I have an image with very low intensity contrast from its background.
The first line between the two arrows is the line with low contrast.
The second line is ok. Please see in the below image.
The original image is as shown below.
I used the following method to enhance the contrast in Gray scale.
First the image is changed to Gray color and used the following method.
cv::Mat temp;
for (int i = 0; i < 1; i++) // number of iterations has to be adjusted
{
cv::threshold(image, temp, 0, 255, CV_THRESH_BINARY| CV_THRESH_OTSU);//
cv::bitwise_and(image, temp, image);
cv::normalize(image, image, 0, 255, cv::NORM_MINMAX, -1, temp);
}
I have image with a little bit higher in contrast in Gray scale, but is there any method better than this in Gray scale or Color?
I would look at histogram equalization, that might serve your needs. Basic (global) equalization or even adaptive can yield great results. Parameters will likely need to be tuned for the adaptive method (using the one from the docs example for now).
I get (global equalization - left; adaptive equalization - right):
Once the equalization is done, you might have better luck with thresholding (though your example is very low contrast):
From there, you can use standard contour/shape matching etc to try to find the location of your 1st black line.
Gotten from
import cv2
import matplotlib.pyplot as plt
import numpy as np
raw_img_load = cv2.imread('H1o8X.png')
imgr = cv2.cvtColor(raw_img_load,cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=30.0, tileGridSize=(8,8))
imgray_ad = clahe.apply(imgr)#adaptive
imgray = cv2.equalizeHist(imgr)#global
res = np.hstack((imgray,imgray_ad))#so we can plot together
plt.imshow(res,cmap='gray')
plt.show()
ret,thresh = cv2.threshold(imgray_ad,150,255,type=cv2.THRESH_BINARY+cv2.THRESH_OTSU)
plt.imshow(thresh,cmap='gray')
plt.show()
EDIT: based on #Doleron's answer, for this particular problem I would recommend using fastNlMeansDenoising (applied before any histogram equalization). Note, however, that it can be a slow function for high-res images/time-sensitive image processing.
The #Antoine Zambelli answer is awsome and it is the correct one. Anyway, I dug some here and and tried to remove the noise previously with fastNlMeansDenoising to improve the final result:
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/photo.hpp"
using namespace cv;
using cv::CLAHE;
int main(int argc, char** argv) {
Mat srcImage = imread("H1o8X.png", CV_LOAD_IMAGE_GRAYSCALE);
imshow("src", srcImage);
Mat denoised;
fastNlMeansDenoising(srcImage, denoised, 10);
Mat image = denoised;
Ptr<CLAHE> clahe = createCLAHE();
clahe->setClipLimit(30.0);
clahe->setTilesGridSize(Size(8, 8));
Mat imgray_ad;
clahe->apply(image, imgray_ad);
Mat imgray;
cv::equalizeHist(image, imgray);
imshow("imgray_ad", imgray_ad);
imshow("imgray", imgray);
Mat thresh;
threshold(imgray_ad, thresh, 150, 255, THRESH_BINARY | THRESH_OTSU);
imshow("thresh", thresh);
Mat result;
Mat kernel = Mat::ones(8, 8, CV_8UC1);
erode(thresh, result, kernel);
imshow("result", result);
waitKey();
return 0;
}

opencv: blend two images with alpha mask

I'm trying to blend two images with OpenCV in the most efficient way. Currently, I have this:
// Input matrices to mix
cv::Mat A(w, h, CV_8UC3);
cv::Mat B(w, h, CV_8UC3);
// Mix factor
cv::Mat alpha(w, h, CV_8UC1);
// Have to multiply alpha channel for mul() function
std::vector<cv::Mat> array{alpha, alpha, alpha};
cv::Mat alpha_multichannel;
cv::merge(array, alpha_multichannel);
cv::Mat result = A.mul(alpha_multichannel, 1./255) + B.mul(cv::Scalar(255, 255, 255) - alpha_multichannel, 1./255);
Currently this loops at least four times over the image (for the alpha_multichannel image, for A.mul, for B.mul and for the sum), although with a custom loop it could be done in one loop.
Is there a better way to do this?

what 2 & 3 mean in this and how can i change them CvMat* rot = cvCreateMat(2,3,CV_32FC1)

What do 2 & 3 mean in this and how can I change them?
CvMat* rot = cvCreateMat(2,3,CV_32FC1)
When I change these two values I get an openCV GUI error handler.
size of input arguments do not match()
in function cvConvertScale.\cxconvert.cpp(1601)
I want to understand what that means
Update:
The code is:
#include <cv.h>
#include <highgui.h>
int main()
{
CvMat* rot = cvCreateMat(2,3,CV_32FC1);
IplImage *src, *dst;
src=cvLoadImage("doda.jpg");
// make acopy of gray image(src)
dst = cvCloneImage( src );
dst->origin = src->origin;
// make dstof zeros
cvZero( dst );
// Compute rotation matrix
double x=0.0;
// loop to get rotation from 0 to 360 by 4 press on anykey
for(int i=1;i<=5;i++)
{
CvPoint2D32f center = cvPoint2D32f(src->width/2,src->height/2);
double angle = 0+x;
double scale = 0.6;
cv2DRotationMatrix( center, angle, scale, rot );
// Do the transformation
cvWarpAffine( src, dst, rot);
cvNamedWindow( "Affine_Transform", 1 );
cvShowImage( "Affine_Transform", dst );
if (i<=4)
x=x+90.0;
else
x=0.0;
cvWaitKey();
}
cvReleaseImage( &dst );
cvReleaseMat( &rot );
return 0;
}
2 and 3 are the row and column counts of the matrix you're creating.
From Introduction to programming with OpenCV:
Allocate a matrix:
CvMat* cvCreateMat(int rows, int cols, int type);
type: Type of the matrix elements. Specified in form
CV_<bit_depth>(S|U|F)C<number_of_channels>. E.g.: CV_8UC1 means an
8-bit unsigned single-channel matrix, CV_32SC2 means a 32-bit signed
matrix with two channels.
Example:
CvMat* M = cvCreateMat(4,4,CV_32FC1);
Changing them is as simple as substituting different values. But I guess you should already know that.
2 = number of rows and 3 = number of columns in your matrix, rot.
Can you post the entire code? Or maybe tell us what you want to achieve? Are you trying to rotate an image?
Also, I'd recommend upgrading to OpenCV 2.0 which has a C++ interface. With the new version, you can extensively use the Mat class which handles everything (matrices,images,etc.) and makes things much simpler.
You get an error using any other shape than 2x3 because it is then meaningless for opencv when you use rot for rotation.
Take a look at Jacob's answer.
He describes the rotation matrix components in details.

Resources