Edit:: made some code change and at least am not getting the empty page error. Update code below.
I am using OpenCV3 and Tesseract and have done some processing on a relatively simple image and I was expecting the ocr part to work smoothly but it's not.
Image:
Code:
Ptr<cv::text::OCRTesseract> ocr =
cv::text::OCRTesseract::create(NULL /*datapath*/, "eng" /*lang*/, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /*whitelist*/, 2 /*oem*/, 10 /*psmode*/);
string output;
vector<Rect> boxes;
vector<string> words;
vector<float> confidences;
ocr->run(gray3, output, &boxes, &words, &confidences, cv::text::OCR_LEVEL_WORD);
Output:
I
Any idea what's going on?
Thanks.
Removing the blobs connected to the borders will help improve tesseract. So we take your image:
You want to invert the image so the character is white and background black:
Mat img = imread("T2.png"); // reading the example image
cvtColor(img, img, CV_RGB2GRAY);
bitwise_not(img, img); // invert the image
Then we want to remove the blobs connected to the borders using the floodFill method
// Remove blobs attached on corners
uchar white(255);
// do top and bottom row
for (int y = 0; y < img.rows; y += img.rows - 1)
{
uchar* row = img.ptr<uchar>(y);
for (int x = 0; x < img.cols; ++x)
{
if (row[x] == white)
{
floodFill(img, Point(x, y), Scalar(0), (Rect*)0, Scalar(), Scalar(200));
}
}
}
// fix left and right sides
for (int y = 0; y < img.rows; ++y)
{
uchar* row = img.ptr<uchar>(y);
for (int x = 0; x < img.cols; x += img.cols - 1)
{
if (row[x] == white)
{
floodFill(img, Point(x, y), Scalar(0), (Rect*)0, Scalar(), Scalar(200));
}
}
}
This will produce the following image:
Running tesseract on this image will result in 'T' instead of 'I'
Hope this helps you solving your problem. :)
Related
so i'm making this project where i'm making the reflection of an image on OpenCV (without using the flip function), and the only problem (i think) to finish it, is that the image that is suppose to come out reflected, is coming out as all blue.
The code i have (i took out the usual part, the problem should be around here):
Mat imageReflectionFinal = Mat::zeros(Size(220,220),CV_8UC3);
for(unsigned int r=0; r<221; r++)
for(unsigned int c=0; c<221; c++) {
Vec3b intensity = image.at<Vec3b>(r,c);
imageReflectionFinal.at<Vec3b>(r,c) = (uchar)(c, -r + (220)/2);
}
///displays images
imshow( "Original Image", image );
imshow("Reflected Image", imageReflectionFinal);
waitKey(0);
return 0;
}
There are some problems with your code. As pointed out, your iteration variables go beyond the actual image dimensions. Do not use hardcoded bounds, you can use inputImage.cols and inputImage.rows instead to obtain the image dimensions.
There’s the variable (a BGR Vec3b) that is set but not used - Vec3b intensity = image.at<Vec3b>(r,c);
Most importantly, it is not clear what you are trying to achieve. The line (uchar)(c, -r + (220)/2); does not give out much info. Also, which direction are you flipping the original image around? X or Y axis?
Here’s a possible solution to flip your image in the X direction:
//get input image:
cv::Mat testMat = cv::imread( "lena.png" );
//Get the input image size:
int matCols = testMat.cols;
int matRows = testMat.rows;
//prepare the output image:
cv::Mat imageReflectionFinal = cv::Mat::zeros( testMat.size(), testMat.type() );
//the image will be flipped around the x axis, so the "target"
//row will start at the last row of the input image:
int targetRow = matRows-1;
//loop thru the original image, getting the current pixel value:
for( int r = 0; r < matRows; r++ ){
for( int c = 0; c < matCols; c++ ) {
//get the source pixel:
cv::Vec3b sourcePixel = testMat.at<cv::Vec3b>( r , c );
//source and target columns are the same:
int targetCol = c;
//set the target pixel
imageReflectionFinal.at<cv::Vec3b>( targetRow , targetCol ) = sourcePixel;
}
//for every iterated source row, decrease the number of
//target rows, as we are flipping the pixels in the x dimension:
targetRow--;
}
Result:
I would like to know how to remove the black border from the following frame in OpenCV using C++
Original Image
Result
Any help would be really appreciated.
To remove some non-black noise I recommend using cv::threshold and morphology closing. Then you can just remove rows and columns which contains (for example) more than 5% non-black pixels.
I tried following code and it works for your example:
int main()
{
const int threshVal = 20;
const float borderThresh = 0.05f; // 5%
cv::Mat img = cv::imread("img.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat thresholded;
cv::threshold(img, thresholded, threshVal, 255, cv::THRESH_BINARY);
cv::morphologyEx(thresholded, thresholded, cv::MORPH_CLOSE,
cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)),
cv::Point(-1, -1), 2, cv::BORDER_CONSTANT, cv::Scalar(0));
cv::imshow("thresholded", thresholded);
cv::Point tl, br;
for (int row = 0; row < thresholded.rows; row++)
{
if (cv::countNonZero(thresholded.row(row)) > borderThresh * thresholded.cols)
{
tl.y = row;
break;
}
}
for (int col = 0; col < thresholded.cols; col++)
{
if (cv::countNonZero(thresholded.col(col)) > borderThresh * thresholded.rows)
{
tl.x = col;
break;
}
}
for (int row = thresholded.rows - 1; row >= 0; row--)
{
if (cv::countNonZero(thresholded.row(row)) > borderThresh * thresholded.cols)
{
br.y = row;
break;
}
}
for (int col = thresholded.cols - 1; col >= 0; col--)
{
if (cv::countNonZero(thresholded.col(col)) > borderThresh * thresholded.rows)
{
br.x = col;
break;
}
}
cv::Rect roi(tl, br);
cv::Mat cropped = img(roi);
cv::imwrite("cropped.jpg", cropped);
return 0;
}
Please note that in order to get the best results on all your samples you may need to adjust some parameters: threshVal and borderThresh.
Also you may want to read good tutorials about thresholding and morphology transformations.
From akarsakov's answer. His will crop out the black parts of the input image. But, it will write this cropped image in grayscale. If you are after colour try changing and adding these lines.
#include "opencv2/opencv.hpp"
using namespace cv;
// Read your input image
Mat img = imread("img.jpg");
// Prepare new grayscale image
Mat input_img_gray;
// Convert to img to Grayscale
cvtColor (img, input_img_gray, CV_RGB2GRAY);
Mat thresholded;
// Threshold uses grayscale image
threshold(input_img_gray, thresholded, threshVal, 255, cv::THRESH_BINARY);
I'd recommend ticking akarsakov's answer because it definitely works. This is just for anyone looking to output a coloured image :)
I'm looking for a way to place on image on top of another image at a set location.
I have been able to place images on top of each other using cv::addWeighted but when I searched for this particular problem, there wasn't any posts that I could find relating to C++.
Quick Example:
200x200 Red Square & 100x100 Blue Square
&
Blue Square on the Red Square at 70x70 (From top left corner Pixel of Blue Square)
You can also create a Mat that points to a rectangular region of the original image and copy the blue image to that:
Mat bigImage = imread("redSquare.png", -1);
Mat lilImage = imread("blueSquare.png", -1);
Mat insetImage(bigImage, Rect(70, 70, 100, 100));
lilImage.copyTo(insetImage);
imshow("Overlay Image", bigImage);
Building from beaker answer, and generalizing to any input images size, with some error checking:
cv::Mat bigImage = cv::imread("redSquare.png", -1);
const cv::Mat smallImage = cv::imread("blueSquare.png", -1);
const int x = 70;
const int y = 70;
cv::Mat destRoi;
try {
destRoi = bigImage(cv::Rect(x, y, smallImage.cols, smallImage.rows));
} catch (...) {
std::cerr << "Trying to create roi out of image boundaries" << std::endl;
return -1;
}
smallImage.copyTo(destRoi);
cv::imshow("Overlay Image", bigImage);
Check cv::Mat::operator()
Note: Probably this will still fail if the 2 images have different formats, e.g. if one is color and the other grayscale.
Suggested explicit algorithm:
1 - Read two images. E.g., bottom.ppm, top.ppm,
2 - Read the location for overlay. E.g., let the wanted top-left corner of "top.ppm" on "bottom.ppm" be (x,y) where 0 < x < bottom.height() and 0 < y < bottom.width(),
3 - Finally, nested loop on the top image to modify the bottom image pixel by pixel:
for(int i=0; i<top.height(); i++) {
for(int j=0; j<top.width(), j++) {
bottom(x+i, y+j) = top(i,j);
}
}
return bottom image.
I am looking for a way to take an image and get masks of all objects in it by color. My goal is to be able to separate similarly colored objects into layers so I can further examine each layer. The plan is to use each mask against the original image to create a histogram of the colors in each object and determine the similarity with other objects in the image. If something is similar enough it will be combined with other objects to form a layer.
The problem is that I can not find a function in opencv to find all objects in an image based on color contiguity. I am sure such an algorithm exists, but it seems to be evading me. Does anyone know of an algorithm or function like this?
The best method that I have found is K-means Clustering. This separates the image into different layers based on color. It uses a k-neighbors algorithm to do so. With this I am able to effectively split the image into several layers that are of similar color.
#define numClusters 7
cv::Mat src = cv::imread("img0.png");
cv::Mat kMeansSrc(src.rows * src.cols, 3, CV_32F);
//resize the image to src.rows*src.cols x 3
//cv::kmeans expects an image that is in rows with 3 channel columns
//this rearranges the image into (rows * columns, numChannels)
for( int y = 0; y < src.rows; y++ )
{
for( int x = 0; x < src.cols; x++ )
{
for( int z = 0; z < 3; z++)
kMeansSrc.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z];
}
}
cv::Mat labels;
cv::Mat centers;
int attempts = 2;
//perform kmeans on kMeansSrc where numClusters is defined previously as 7
//end either when desired accuracy is met or the maximum number of iterations is reached
cv::kmeans(kMeansSrc, numClusters, labels, cv::TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 8, 1), attempts, KMEANS_PP_CENTERS, centers );
//create an array of numClusters colors
int colors[numClusters];
for(int i = 0; i < numClusters; i++) {
colors[i] = 255/(i+1);
}
std::vector<cv::Mat> layers;
for(int i = 0; i < numClusters; i++)
{
layers.push_back(cv::Mat::zeros(src.rows,src.cols,CV_32F));
}
//use the labels to draw the layers
//using the array of colors, draw the pixels onto each label image
for( int y = 0; y < src.rows; y++ )
{
for( int x = 0; x < src.cols; x++ )
{
int cluster_idx = labels.at<int>(y + x*src.rows,0);
layers[cluster_idx].at<float>(y, x) = (float)(colors[cluster_idx]);;
}
}
std::vector<cv::Mat> srcLayers;
//each layer to mask a portion of the original image
//this leaves us with sections of similar color from the original image
for(int i = 0; i < numClusters; i++)
{
layers[i].convertTo(layers[i], CV_8UC1);
srcLayers.push_back(cv::Mat());
src.copyTo(srcLayers[i], layers[i]);
}
I suggest you convert the image to the HSV-space (Hue-Saturation-Value). Then make a histogram based on the Hue value to find thresholds online, or define them before (depends if this is a general problem or a given one).
Crate one-channel images for each layer you want to form. (set them as black)
Then then use the HSV-image and mark a layer based on the threshold values. You might want to add some constant thresholds for value and saturation too (to avoid dark and light areas)
Does this make sense to you?
I think that you should proceed in the following proceess:
Smooth you image if it has too much details.
find edges
Find all contours
Try to find the color of each contour..lets say you want to keep all contours which are red. So, keep only those contours which are red.
Once you find the contours which you want to keep, then create a mask image based upon the contours you want to keep.
Using mask image, extract the required objects from the original image.
I'm new with OpenCV and I'm using it for change the luminosity of an image.
In my image, here: https://docs.google.com/file/d/0B9LaMgEERnMxQUNKbndBODJ5TXM/edit, there's a big space reflecting the light of the ambiance just in one part of it. At first, I could change all the luminosity on image. Now, I'm trying to reduce this space, which means in a specific place of the image, using the V of HSV, here is the code for that:
enter code here
Mat newImg;
cvtColor(img, newImg, CV_BGR2HSV);
imwrite("C:/Users/amanda.brito/Desktop/test.jpg", newImg);
vector<Mat> hsv_planes;
split(newImg, hsv_planes); //geting the color plans of image
int param = -70; // the value that I'm seting for V
for (int y = 0; y < newImg.rows; y++) {
for (int x = 0; x < newImg.cols; x++) {
Vec3b pixel = hsv_planes[2].at<Vec3b>(y, x);
pixel[0] = 0;
pixel[1] = 0;
pixel[2] = param;
hsv_planes[2].at<Vec3b>(y, x) = pixel;
}
}
merge(hsv_planes, newImg);
Mat imagem;
cvtColor(newImg, imagem, CV_HSV2BGR);
imwrite("C:/Users/amanda.brito/Desktop/final.jpg", imagem);
Well, with this or nothing happen or the the compiler stops the program.
I already looked everywhere but without luck. What am I doing wrong?
Since now, thanks for your help.