I just realised that there is nothing on the web, after much searching about how to access a pixel's intensity value in OpenCv. A grayscale image.
Most online searches are about how to access BGR values of a colour image, like this one: Accessing certain pixel RGB value in openCV
image.at<> is basically for 3 channels, namely the BGR, out of curiousity, is there another similar method from OpenCV of accessing a certain pixel value of a grayscale image?
You can use image.at<uchar>(j,i) to acces a pixel value of a grayscale image.
cv::Mat::at<>() function is for every type of image, whether it is a single channel image or multi-channel image. The type of value returned just depends on the template argument provided to the function.
The value of grayscale image can be accessed like this:
//For 8-bit grayscale image.
unsigned char value = image.at<unsigned char>(row, column);
Make sure to return the correct data type depending on the image type (8u, 16u, 32f etc.).
For IplImage* image, you can use
uchar intensity = CV_IMAGE_ELEM(image, uchar, y, x);
For Mat image, you can use
uchar intensity = image.at<uchar>(y, x);
at(y,x)]++;
for(int i = 0; i < 256; i++)
cout<<histogram[i]<<" ";
// draw the histograms
int hist_w = 512; int hist_h = 400;
int bin_w = cvRound((double) hist_w/256);
Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(255, 255, 255));
// find the maximum intensity element from histogram
int max = histogram[0];
for(int i = 1; i < 256; i++){
if(max < histogram[i]){
max = histogram[i];
}
}
// normalize the histogram between 0 and histImage.rows
for(int i = 0; i < 255; i++){
histogram[i] = ((double)histogram[i]/max)*histImage.rows;
}
// draw the intensity line for histogram
for(int i = 0; i < 255; i++)
{
line(histImage, Point(bin_w*(i), hist_h),
Point(bin_w*(i), hist_h - histogram[i]),
Scalar(0,0,0), 1, 8, 0);
}
// display histogram
namedWindow("Intensity Histogram", CV_WINDOW_AUTOSIZE);
imshow("Intensity Histogram", histImage);
namedWindow("Image", CV_WINDOW_AUTOSIZE);
imshow("Image", image);
waitKey();
return 0;
}
Related
I am learning image processing with OpenCV in C++. To implement a basic down-sampling algorithm I need to work on the pixel level -to remove rows and columns. However, when I assign values with mat.at<>(i,j) other values are assign - things like 1e-38.
Here is the code :
Mat src, dst;
src = imread("diw3.jpg", CV_32F);//src is a 479x359 grayscale image
//dst will contain src low-pass-filtered I checked by displaying it works fine
Mat kernel;
kernel = Mat::ones(3, 3, CV_32F) / (float)(9);
filter2D(src, dst, -1, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
// Now I try to remove half the rows/columns result is stored in downsampled
Mat downsampled = Mat::zeros(240, 180, CV_32F);
for (int i =0; i<downsampled.rows; i ++){
for (int j=0; j<downsampled.cols; j ++){
downsampled.at<uchar>(i,j) = dst.at<uchar>(2*i,2*j);
}
}
Since I read here OpenCV outputing odd pixel values that for cout I needed to cast, I wrote downsampled.at<uchar>(i,j) = (int) before dst.at<uchar> but it does not work also.
The second argument to cv::imread is cv::ImreadModes, so the line:
src = imread("diw3.jpg", CV_32F);
is not correct; it should probably be:
cv::Mat src_8u = imread("diw3.jpg", cv::IMREAD_GRAYSCALE);
src_8u.convertTo(src, CV_32FC1);
which will read the image as 8-bit grayscale image, and will convert it to floating point values.
The loop should look something like this:
Mat downsampled = Mat::zeros(240, 180, CV_32FC1);
for (int i = 0; i < downsampled.rows; i++) {
for (int j = 0; j < downsampled.cols; j++) {
downsampled.at<float>(i,j) = dst.at<float>(2*i,2*j);
}
}
note that the argument to cv::Mat::zeros is CV_32FC1 (1 channel, with 32-bit floating values), so Mat::at<float> method should be used.
I have been trying lot to get an undistorted image without interpolation. But when executed the below code i get some weird image.I am using the function initUndistortRectifyMap which gives the mapx and mapy of type CV_16SC2 later using the convertMaps function i am converting the mapx and mapy to type CV_32FC1.I have been trying to debug the reason but couldnot find anything helpful.
The distorted image
image after applying undistort without interpolation
int main()
{
Mat Cam1MatrixParam, Cam1Distortion;
Mat cf1;
cf1=imread("cam1.distort1.jpg", CV_LOAD_IMAGE_COLOR);
Size imagesize = cf1.size();
FileStorage fs1("cameracalibration.xml", FileStorage::READ);
fs1["camera_matrix"] >> Cam1MatrixParam;
fs1["distortion_coefficients"] >> Cam1Distortion;
Mat R = Mat::eye(3, 3, CV_32F) * 1;
int width = cf1.cols;
int height = cf1.rows;
Mat undistorted = Mat(height, width, CV_8UC3);
Mat mapx = Mat(height, width, CV_32FC1);
Mat mapy = Mat(height, width, CV_32FC1);
initUndistortRectifyMap(Cam1MatrixParam, Cam1Distortion, Cam1MatrixParam, R, imagesize, CV_16SC2, mapx, mapy);
convertMaps(mapx, mapy, mapx, mapy, CV_32FC1, false);
for (int j = 0; j < height; j++)
{
for ( int i = 0; i < width; i++)
{
undistorted.at<uchar>(mapy.at<float>(j, i), mapx.at<float>(j, i)) = cf1.at<uchar>(j, i);
}
}
imwrite("cam1.undistortimage.png", undistorted);
}
image with this version of code
undistorted.at(j, i) = cf1.at(mapy.at(j, i), mapx.at(j, i));
image with undistort function(remap with nearest interpolation)
It looks like instead of undoing the distortion it applies it once more.
mapx and mapy map from the display coordinates to the photo coordinates.
undistorted.at<cv::Vec3b>(j, i) = distort.at<cv::Vec3b>(mapy.at<float>(j, i), mapx.at<float>(j, i));
You can interpret this code as: for each display coordinate {j, i} find its corresponding (distorted) coordinate in the photo and then copy the pixel.
you are using color images (cv::Vec3b) so try instead:
undistorted.at<cv::Vec3b>(mapy.at<float>(j, i), mapx.at<float>(j, i)) = cf1.at<cv::Vec3b>(j, i);
maybe combined with the answer of Maxim Egorushkin if undistort map is reverse
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 want to replace grayscale color with rgb color for each pixel. I am new to open cv.
GrayScale val = 25
replace by
rgb value = 0,0,255
This is how to do it with a LUT:
I create some dummy image with grayscale values:
cv::Mat input = cv::Mat(512,512,CV_8UC1, cv::Scalar(0));
for(int j=0; j<input.rows; ++j)
for(int i=0; i<input.cols; ++i)
{
input.at<unsigned char>(j,i) = i/2;
}
Then I set up the LUT:
// create replacement look-up-table:
// 1. basic => gray values of given intensity
cv::Mat lookUpTable(1, 256, CV_8UC3);
for( int i = 0; i < 256; ++i)
lookUpTable.at<cv::Vec3b>(0,i) = cv::Vec3b(i,i,i);
// 2. replace whatever colors you want:
lookUpTable.at<cv::Vec3b>(0,25) = cv::Vec3b(25,0,0);
lookUpTable.at<cv::Vec3b>(0,100) = cv::Vec3b(0,255,0); // means to replace each gray == (100) value by a (0,255,0) color value after LUT call
lookUpTable.at<cv::Vec3b>(0,115) = cv::Vec3b(255,0,0);
lookUpTable.at<cv::Vec3b>(0,200) = cv::Vec3b(0,100,255);
afterwards: convert input to COLOR and call LUT:
// unfortunately, we have to convert to color, because OpenCV doesnt allow LUT from single channel to 3 channel directly. (LUT must have same number of channels as input)
cv::Mat input_3channels;
cv::cvtColor(input, input_3channels, CV_GRAY2BGR);
cv::Mat output;
cv::LUT(input_3channels, lookUpTable, output);
Giving me that output for this input:
In your comments you like to replace "black" color by "red" color. You have to define what you mean by "black". In theory, black color is intensity = 0 and everything > 0 is just a very dark gray. So here I show how to set some ranges to a color:
cv::Mat lookUpTable(1, 256, CV_8UC3);
for( int i = 0; i < 256; ++i)
lookUpTable.at<cv::Vec3b>(0,i) = cv::Vec3b(i,i,i);
// 2. replace "black color" by red:
// you have to define what black means. If you mean pure black (intensity == 0) then use this:
//lookUpTable.at<cv::Vec3b>(0,0 /* intensity == 0 */) = cv::Vec3b(0,0,255);
// if you mean something that appears mostly black for a human eye, use something like this:
unsigned char startIntensity = 0; // start at intensity 0 (black)
unsigned char endIntensity = 20; // the higher this value, the more "dark grey" will be replaced by red too
cv::Vec3b replacementColor = cv::Vec3b(0,0,255); // red
for(int i=startIntensity ; i < endIntensity ; ++i)
lookUpTable.at<cv::Vec3b>(0,i) = replacementColor;
for example giving this result:
Here you have a small test code (the key is inRange):
create a rectangle in a gray image (with value of 25)
create a mask where the 25 values are (inRange)
transform the gray image into color image
change the value of the pixels where the mask is
cv::Mat image = cv::Mat::zeros(cv::Size(200,200), CV_8U); //create zero image
cv::rectangle(image, cv::Rect(50, 50, 100, 100), cv::Scalar::all(25), -1); //write a rectangle
cv::imshow("image", image); //show image
cv::Mat mask;
cv::inRange(image,25,25,mask); //mask the 25 values
cv::cvtColor(image, image, CV_GRAY2BGR); //convert gray image to BGR
cv::Scalar red(0,0,255);
image.setTo(red,mask); //change all 25 values into red color
cv::imshow("colorImage", image); //show result image
cv::waitKey(0); //wait until you press a key
I'm using OpenCV2.4.8.2 on Mac OS 10.9.5.
I have the following snippet of code:
static void compute_weights(const vector<Mat>& images, vector<Mat>& weights)
{
weights.clear();
for (int i = 0; i < images.size(); i++) {
Mat image = images[i];
Mat mask = Mat::zeros(image.size(), CV_32F);
int x_start = (i == 0) ? 0 : image.cols/2;
int y_start = 0;
int width = image.cols/2;
int height = image.rows;
Mat roi = mask(Rect(x_start,y_start,width,height)); // Set Roi
roi.setTo(1);
weights.push_back(mask);
}
}
static void blend(const vector<Mat>& inputImages, Mat& outputImage)
{
int maxPyrIndex = 6;
vector<Mat> weights;
compute_weights(inputImages, weights);
// Find the fused pyramid:
vector<Mat> fused_pyramid;
for (int i = 0; i < inputImages.size(); i++) {
Mat image = inputImages[i];
// Build Gaussian Pyramid for Weights
vector<Mat> weight_gaussian_pyramid;
buildPyramid(weights[i], weight_gaussian_pyramid, maxPyrIndex);
// Build Laplacian Pyramid for original image
Mat float_image;
inputImages[i].convertTo(float_image, CV_32FC3, 1.0/255.0);
vector<Mat> orig_guassian_pyramid;
vector<Mat> orig_laplacian_pyramid;
buildPyramid(float_image, orig_guassian_pyramid, maxPyrIndex);
for (int j = 0; j < orig_guassian_pyramid.size() - 1; j++) {
Mat sized_up;
pyrUp(orig_guassian_pyramid[j+1], sized_up, Size(orig_guassian_pyramid[j].cols, orig_guassian_pyramid[j].rows));
orig_laplacian_pyramid.push_back(orig_guassian_pyramid[j] - sized_up);
}
// Last Lapalcian layer is the same as the Gaussian layer
orig_laplacian_pyramid.push_back(orig_guassian_pyramid[orig_guassian_pyramid.size()-1]);
// Convolve laplacian original with guassian weights
vector<Mat> convolved;
for (int j = 0; j < maxPyrIndex + 1; j++) {
// Create 3 channels for weight gaussian pyramid as well
vector<Mat> gaussian_3d_vec;
for (int k = 0; k < 3; k++) {
gaussian_3d_vec.push_back(weight_gaussian_pyramid[j]);
}
Mat gaussian_3d;
merge(gaussian_3d_vec, gaussian_3d);
//Mat convolved_result = weight_gaussian_pyramid[j].clone();
Mat convolved_result = gaussian_3d.clone();
multiply(gaussian_3d, orig_laplacian_pyramid[j], convolved_result);
convolved.push_back(convolved_result);
}
if (i == 0) {
fused_pyramid = convolved;
} else {
for (int j = 0; j < maxPyrIndex + 1; j++) {
fused_pyramid[j] += convolved[j];
}
}
}
// Blending
for (int i = (int)fused_pyramid.size()-1; i > 0; i--) {
Mat sized_up;
pyrUp(fused_pyramid[i], sized_up, Size(fused_pyramid[i-1].cols, fused_pyramid[i-1].rows));
fused_pyramid[i-1] += sized_up;
}
Mat final_color_bgr;
fused_pyramid[0].convertTo(final_color_bgr, CV_32F, 255);
final_color_bgr.copyTo(outputImage);
imshow("final", outputImage);
waitKey(0);
imwrite(outputImagePath, outputImage);
}
This code is doing some basic pyramid blending for 2 images. The key issues are related to imshow and imwrite in the last line. They gave me drastically different results. I apologize for displaying such a long/messy code, but I am afraid this difference is coming from some other parts of the code that can subsequently affect the imshow and imwrite.
The first image shows the result from imwrite and the second image shows the result from imshow, based on the code given. I'm quite confused about why this is the case.
I also noticed that when I do these:
Mat float_image;
inputImages[i].convertTo(float_image, CV_32FC3, 1.0/255.0);
imshow("float image", float_image);
imshow("orig image", image);
They show exactly the same thing, that is they both show the same picture in the original rgb image (in image).
IMWRITE functionality
By default, imwrite, converts the input image into Only 8-bit (or 16-bit unsigned (CV_16U) in case of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with ‘BGR’ channel order) images can be saved using this function.
So whatever format you feed in for imwrite, it blindly converts into CV_8U with a range 0(black) - 255(white) in BGR format.
IMSHOW - problem
So when noticed your function, fused_pyramid[0].convertTo(final_color_bgr, CV_32F, 255); fused_pyramid is already under mat type 21 (floating point CV_32F). You tried to convert into floating point with a scale factor 255. This scaling factor 255 caused the problem # imshow. Instead to visualize, you can directly feed in fused_pyramid without conversion as already it is scaled to floating point between 0.0(black) - 1.0(white).
Hope it helps.