I'm creating an application of watermarking using opencv, I'm not able to set background of image as transparent.
I'm using this code Scalar colorScalar = new Scalar(255,255,255,0);
Can any body help me how to make background transparent. I'm using PNG format image.
targetMat = new Mat(targetSize, scaledImage.type(), colorScalar);
Mat waterSubmat = targetMat.submat((int)offsetY,scaledImage.height(), (int)offsetX, scaledImage.width());
scaledImage.copyTo(waterSubmat);
center = new org.opencv.core.Point(pivotX, pivotY);
Mat rotImage = Imgproc.getRotationMatrix2D(center, degreevaluechange, 1);
Mat resultMat = new Mat(2,3, CvType.CV_32FC1);
colorScalar = new Scalar(255,255,255,0);
Imgproc.warpAffine(targetMat, resultMat, rotImage, targetSize, Imgproc.INTER_AREA, Imgproc.BORDER_CONSTANT, colorScalar);
scaledImage = resultMat.clone();
If you want to load your PNG image with the alpha channel and therefore load your image with transparenty, you have to use this code:
imread("image.png",-1)
You can find more informations in the opencv documentation here:
http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=imread#imread
As you see in the documentation provided by Maximus. You need to create a 4 channel Mat:
Mat* targetMat = new Mat(targetSize, CV_8UC4, colorScalar);
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);
try {
imwrite("alpha.png", targetMat, compression_params);
}
catch (runtime_error& ex) {
fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
return 1;
}
Then add the parameters and write. (This code is from the documentation)
Related
I have two images (CV_8UC3) and a mask (CV_8UC1) all of the same size and I would like to apply the mask to one of the images and put it on top of the other:
const cv = require('opencv4nodejs');
const bg = cv.imread('./bg.jpg').cvtColor(cv.COLOR_RGB2RGBA);
//Loading the foreground image in RGB
const fg = cv.imread('./fg.jpg');
//Generating the mask with only one channel
const mask = cv.imread('./mask.jpg').cvtColor(cv.COLOR_RGB2GRAY);
const fgChannels = fg.split();
fgChannels.push(mask);
const maskedFg = new cv.Mat(fgChannels);
const output = cv.addWeighted(bg, 1, maskedFg, 1, 0).cvtColor(cv.COLOR_RGBA2RGB);
cv.imwrite('./output.jpg', output);
And here how it works. First the bg.jpg file:
Then the fg.jpg file:
The mask.jpg file:
And finally, the output.jpg file:
My problem with the output is that I was not expecting to see any part of the background image unless they are underneath the tunnel's opening. Can someone please help me find the solution?
Apparently, OpenCV does not have a function to do this directly. Instead, you have to use a combination of functions to do it:
const cv = require('opencv4nodejs');
const bg = cv.imread('./bg.jpg').convertTo(cv.CV_32FC3, 1.0 / 255);
const fg = cv.imread('./fg.jpg').convertTo(cv.CV_32FC3, 1.0 / 255);
const mask = cv.imread('./mask.jpg').convertTo(cv.CV_32FC3, 1.0 / 255);
const allOnes = new cv.Mat(mask.rows, mask.cols, cv.CV_32FC3, [1.0, 1.0, 1.0]);
const invMask = allOnes.sub(mask);
const output = mask.hMul(fg).add(invMask.hMul(bg));
cv.imwrite('./output.jpg', output);
What exactly are you trying to achieve? Only add the masked part from bg to fg or remove the masked part on fg before that?
I don't think you are applying the mask correctly.
The following approach should work to apply the mask:
If the mask is binary: use bitwise_and with bg and mask
If the mask needs to be grayscale: use element-wise multiplication
The resulting masked_bg will only contain the masked part of the image.
Also note that output.jpg is too bright because you are simply adding the two images on top of each other. You could change the weights to 0.5 each or make sure the colored parts of both images never overlap.
I am currently using tesseract to scan receipts. The quality wasn't good so I read this article on how to improve it: https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality#noise-removal. I implemented resizing, deskewing(aligning), and gaussian blur. But none of them seem to have a positive effect on the accuracy of the OCR except the deskewing. Here is my code for resizing and gaussian blur. Am I doing anything wrong? If not, what else can I do to help?
Code:
+(UIImage *) prepareImage: (UIImage *)image{
//converts UIImage to Mat format
Mat im = cvMatWithImage(image);
//grayscale image
Mat gray;
cvtColor(im, gray, CV_BGR2GRAY);
//deskews text
//did not provide code because I know it works
Mat preprocessed = preprocess2(gray);
double skew = hough_transform(preprocessed, im);
Mat rotated = rot(im,skew* CV_PI/180);
//resize image
Mat scaledImage = scaleImage(rotated, 2);
//Guassian Blur
GaussianBlur(scaledImage, scaledImage, cv::Size(1, 1), 0, 0);
return UIImageFromCVMat(scaledImage);
}
// Organization -> Resizing
Mat scaleImage(Mat mat, double factor){
Mat resizedMat;
double width = mat.cols;
double height = mat.rows;
double aspectRatio = width/height;
resize(mat, resizedMat, cv::Size(width*factor*aspectRatio, height*factor*aspectRatio));
return resizedMat;
}
Receipt:
If you read the Tesseract documentation you will see that tesseract engine works best with texts in a single line in a square. Passing it the whole receipt image reduces the engine's accuracy. What you need to do is use the new iOS framework CITextFeature to detect texts in your receipt into multiple blocks of images. Then only you can pass those images to tesseract for processing.
Suppose we have the following color:
const Scalar TRANSPARENT2 = Scalar(255, 0, 255,0);
which is magenta but fully transparent: alpha = 0 (to be fully opaque is 255).
Now I made the following test based on:
http://blogs.msdn.com/b/lucian/archive/2015/12/04/opencv-first-version-up-on-nuget.aspx
WriteableBitmap^ Grabcut::TestTransparent()
{
Mat res(400,400, CV_8UC4);
res.setTo(TRANSPARENT2);
WriteableBitmap^ wbmp = ref new WriteableBitmap(res.cols, res.rows);
IBuffer^ buffer = wbmp->PixelBuffer;
unsigned char* dstPixels;
ComPtr<IBufferByteAccess> pBufferByteAccess;
ComPtr<IInspectable> pBuffer((IInspectable*)buffer);
pBuffer.As(&pBufferByteAccess);
pBufferByteAccess->Buffer(&dstPixels);
memcpy(dstPixels, res.data, res.step.buf[1] * res.cols * res.rows);
return wbmp;
}
The issue I have is that the image created is not fully transparent, it has a bit of alpha:
I understand there is a fila in the memcpy data, but I am not really sure about how to solve this. any idea to get it to alpha 0?
more details
To see I saving the image could then read and test if it works, I saw that the imwrite contains an snippet about transparency like in the image, but well imwrite is not implemented yet. But the transparency method is not working neither.
Any light with this snippet?
Thanks.
Finally I did the conversion in the C# code, first avoid calling CreateAlphaMat.
Then what I did is use a BitmapEncoder to convert data:
WriteableBitmap wb = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight);
using (IRandomAccessStream stream = new InMemoryRandomAccessStream())
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
Stream pixelStream = bitmap.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied,
(uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight, 96.0, 96.0, pixels);
await encoder.FlushAsync();
wb.SetSource(stream);
}
this.MainImage.Source = wb;
where bitmap is the WriteableBitmap from the OpenCV result. And now the image is fully transparent.
NOTE: Do not use MemoryStream and then .AsRandomAccessStream because it won't FlushAsync
I would like to know what is the problem in below code, since it only appears only part of the Gray image as Binary image!
cv::Mat gry = cv::imread("image_gray.jpg");
cv::Mat bin(gry.size(), gry.type());
for (int i=0; i<gry.rows ;i++)
{
for (int j=0; j<gry.cols ;j++)
{
if (gry.at<uchar>(i,j)>=100)
bin.at<uchar>(i,j)=255;
else
bin.at<uchar>(i,j)=0;
}
}
cv::namedWindow("After", cv::WINDOW_AUTOSIZE);
cv::imshow("After",bin);
waitKey(0);
cvDestroyWindow( "After" );
imwrite("binary_image.bmp", bin);
Your problem is in cv::imread.
The function assumes it should load the image as a color image, if you want to load it as a garyscale image, you should call the function as follows:
cv::imread(fileName, CV_LOAD_IMAGE_GRAYSCALE)
By the way, the reason you only see part of the image, is because the image is simply bigger than a uchar for each pixel. (and you end up iterating only over part of it).
it would be easier if you use use the OpenCV function:
cv::threshold(image_src, image_dst, 200, 255, cv::THRESH_BINARY);
This piece of code set as black value (255) all those pixels which have as original value 200.
I created a drawing application where I allow the user to draw and save the image to later reload to continue drawing. Essentially, I'm passing the drawing as a bitmap to the JNI layer to be saved and the same to load a previous drawing.
I'm using OpenCv to write and read to png file.
I'm noticing something weird in terms of the transparencies of the image. It almost seems as the transparency is being calculated against a black color on OpenCv? Take a look a the images attached, the contain lines that have transparencies.
Correct transparency by passing int array to native code, no color conversion needed:
Darkened transparency by passing Bitmap object to native code, color conversion needed:
What could potentially be happening?
Saving image using native Bitmap get pixel methods:
if ((error = AndroidBitmap_getInfo(pEnv, jbitmap, &info)) < 0) {
LOGE("AndroidBitmap_getInfo() failed! error:%d",error);
}
if (0 == error)
{
if ((error = AndroidBitmap_lockPixels(pEnv, jbitmap, &pixels)) < 0) {
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", error);
}
}
if (0 == error)
{
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888)
{
LOGI("ANDROID_BITMAP_FORMAT_RGBA_8888");
}
else
{
LOGI("ANDROID_BITMAP_FORMAT %d",info.format);
}
Mat bgra(info.height, info.width, CV_8UC4, pixels);
Mat image;
//bgra.copyTo(image);
// fix pixel order RGBA -> BGRA
cvtColor(bgra, image, COLOR_RGBA2BGRA);
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(3);
// save image
if (!imwrite(filePath, image, compression_params))
{
LOGE("saveImage() -> Error saving image!");
error = -7;
}
// release locked pixels
AndroidBitmap_unlockPixels(pEnv, jbitmap);
}
Saving image using native int pixel array methods:
JNIEXPORT void JNICALL Java_com_vblast_smasher_Smasher_saveImageRaw
(JNIEnv *pEnv, jobject obj, jstring jFilePath, jintArray jbgra, jint options, jint compression)
{
jint* _bgra = pEnv->GetIntArrayElements(jbgra, 0);
const char *filePath = pEnv->GetStringUTFChars(jFilePath, 0);
if (NULL != filePath)
{
Mat image;
Mat bgra(outputHeight, outputWidth, CV_8UC4, (unsigned char *)_bgra);
bgra.copyTo(image);
if (0 == options)
{
// replace existing cache value
mpCache->insert(filePath, image);
}
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(compression);
// save image
if (!imwrite(filePath, image))
{
LOGE("saveImage() -> Error saving image!");
}
}
pEnv->ReleaseIntArrayElements(jbgra, _bgra, 0);
pEnv->ReleaseStringUTFChars(jFilePath, filePath);
}
Update 05/25/12:
After a little more research I'm finding out that this issue does not happen if I get the int array of pixels from the bitmap and pass that directly to the JNI as opposed to what I do currently which is pass the entire Bitmap to the JNI layer then get the pixels and use cvtColor to convert pixels properly. Am I using the right pixel conversion?
There are two ways representing alpha in an RGBA pixel, premultiplied or not. With premultiplication, the R, G, and B values are multiplied by the percentage of alpha: color = (color * alpha) / 255. This simplifies a lot of blending calculations and is often used internally in imaging libraries. Before saving out to a format that doesn't use premultiplied alpha, such as PNG, the color values must be "unmultiplied": color = (255 * color) / alpha. If it is not, the colors will look too dark; the more transparent the color, the darker it will be. That looks like the effect you're seeing here.
There is nothing called as transparent image in opencv. The foreground and the background images are mixed appropriately to give the illusion of transparency. Check this to see how its done.