How to simply get RGB values in OpenCV? - c++

I would like to obtain the RGB values for any pixel I choose or loop. This is currently how I achieve it.
Vec3b color = img.at<Vec3b>(Point(i, j));
and for the loop
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Vec3b color = img.at<Vec3b>(Point(i,j));
img.at<Vec3b>(Point(i, j)) = color;
}
}
But when I apply the Canny/cvtColor function it all messes up. An Unhandled exception at memory location pops up.
I ran more tests, and found that the loop just works fine with the height, but as for the width it works only up (1/3) of the actual width. Most likely to do with Vec3b.
One such solution suggested
unsigned char color = img.at<unsigned char>(Point(i,j));
or
Vec<uchar, 3> color = img2.at <uchar>(Point(i,j));
But in uchar cases, how can i obtain individual RGB from color and how to set color back to pixel?
FULL CODE :
int main() {
VideoCapture cap("Assets/test2.mp4");
if (!cap.isOpened()) {
std::cout << "Problem in reading" << std::endl;
return -1;
}
while (1) {
Mat frame;
cap >> frame;
if (frame.empty()) {
break;
}
frame = Imgfn(frame);
imshow("FRAME", frame);
char c = (char)waitKey(100);
if (c == 27) {
break;
}
}
cap.release();
destroyAllWindows();
return 0;
}
Mat Imgfn(Mat img) {
int width = img.size().width , height = img.size().height;
cvtColor(img, img, COLOR_BGR2GRAY);
GaussianBlur(img, img, Size(3,3),0,0);
Canny(img, img, 50, 150);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Vec3b color = img.at<Vec3b>(Point(i, j));
color[0] = 0; color[1] = 255; color[2] = 0;
img.at<Vec3b>(Point(i, j)) = color;
// so **Obtain, select, set** color
}
}
return img;
}
[About the mp4][1]
[1]: https://i.stack.imgur.com/9hOBV.png
EDIT - FOUND THE SOLUTION - from one of the answers suggested below. The problem is when cvtColor convert to gray is performed it brings down to just 1 channel from 3. Mat.channels() can be used to check. so a simple uchar would work fine. if 3 channels of RGB are required then one could simply created their own RGB to GRAY function

Currently, how do you choose the type such as { Vec3b, unsigned char, ... } ?
If you access pixels in a known Mat , you know the type at the time and you can use that. Just do it so.
Otherwise, if you are talking about a situation in which you don't know the type of pixels in Mat, you can use Mat::depth(), Mat::channels(), etc to determine the type.

Related

Creating trackbar changing brightness of image OpenCV

I've got a problem with creating a trackbar which would adjust brightness of the displayed picture.
This is my code (part which is involved with brightness):
int brightness_value = 25; //global values
const int max_value = 255;
int main()
{
Mat brightImage;
srcImage.copyTo(brightImage);
namedWindow("Bright Image");
moveWindow("Bright Image", 300, 600);
createTrackbar("Brightness", "Bright Image", &brightness_value, max_value);
for (int i = 0; i < brightImage.rows; i++)
{
for (int j = 0; j < brightImage.cols; j++)
{
Vec3b pixelColor;
pixelColor = brightImage.at<Vec3b>(Point(j, i));
for (int k = 0; k < 3; k++) //vector with 3 byte entries
{
if (pixelColor[k] + getTrackbarPos("Brightness", "Bright Image") > 255)
pixelColor[k] = 255;
else
pixelColor[k] += getTrackbarPos("Brightness", "Bright Image");
brightImage.at<Vec3b>(Point(j, i)) = pixelColor;
}
}
}
imshow("Bright Image", brightImage);
waitKey(0);
return 0;
}
This way the brightness of the image is adjusted only once, when the program starts. But when I want to change it with the trackbar nothing happens. Where is the problem, how should I do it so the brightness will change every time I move the trackbar?
Thanks for any help :)
And that's the result: (on the left original image, on the right with changed brightness)
createTrackbar takes a pointer to callback function which is called when position of trackbar is being changed. In this callback you should redrawn your image with changed brightness level and refresh window by imshow. Such callback takes a pointer to void data - it enables you to pass any data you want to use when redrawing your image, in this case it should be pointer to output image (and probably a pointer to source image - you should always add new brightness level to original image, not modified one):
struct Params {
cv::Mat* src;
cv::Mat* dest;
};
void makeBrightness(int pos, void* data) {
Params* params = (Params*)data;
for (int i = 0; i < params->src->rows; i++) {
for (int j = 0; j < params->src->cols; j++) {
Vec3b pixelColor;
pixelColor = params->src->at<Vec3b>(Point(j, i));
for (int k = 0; k < 3; k++) {
if (pixelColor[k] + pos > 255)
pixelColor[k] = 255;
else
pixelColor[k] += pos;
params->dest->at<Vec3b>(Point(j, i)) = pixelColor;
}
}
}
imshow("Bright Image", *(params->dest));
}
int main()
{
int brightness_value = 25; //global values
const int max_value = 255;
Mat srcImage = cv::imread("D:/lena.jpg");
Mat brightImage;
srcImage.copyTo(brightImage);
namedWindow("Bright Image");
moveWindow("Bright Image", 300, 600);
Params params;
params.src = &srcImage;
params.dest = &brightImage;
createTrackbar("Brightness", "Bright Image", &brightness_value, max_value, makeBrightness, &params);
makeBrightness(brightness_value, &params); // for first painting your image
waitKey(0);
return 0;

Opencv only process the parts of image

I want to make a negative transformation for the image which is a very simple program.
But when I run the program. I want to transform all of the pixels in the image, but only 1/3 parts of that are processed. I don't make sure where is wrong. all the code I followed the book. But the result is different.
I think there is something wrong about the columns, but when I change the value of I.cols in negativeImage function with the actual value of image. the output still keep the same. only 1/3 parts of image are processed. If I 3 times the I.cols all of the pixels in the iamge could be processed.
vector<uchar> getNegativeLUT() {
vector<uchar> LUT(256, 0);
for (int i = 0; i < 256; ++i)
LUT[i] = (uchar)(255 - i);
return LUT;
}
void negativeImage(Mat& I) {
vector<uchar> LUT = getNegativeLUT();
for (int i = 0; i < I.rows; ++i) {
for (int j = 0; j < I.cols; ++j) {
I.at<uchar>(i, j) = LUT[I.at<uchar>(i, j)];
//stack overflow
}
}
}
int main() {
Mat image = imread("1.png");
Mat processed_image2 = image.clone();
negativeImage(processed_image2);
printf("%d", image.cols);
imshow("Input Image", image);
imshow("Negative Image", processed_image2);
waitKey(0);
return 0;
}
Output Image
You need to put correct type with at<> operator. Your PNG image has to be converted to 8UC1 to then use uchar type to access each pixel. I suppose your image has 3 channels, so you only iterate over 1/3 of the image. Also, I suggest you to use ptr<> operator in rows loop and then access to pixel as an array.
Mat M;
cvtColor(I, M, CV_BGR2GRAY);
// M is CV_8UC1 type
for(int i = 0; i < M.rows; i++)
{
uchar* p = M.ptr<uchar>(i);
for(int j = 0; j < I.cols; j++)
{
p[j] = LUT[p[j]];
}
}
EDIT: you should use cv::LUT instead of doing it yourself.
cv::Mat lut(1, 256, CV_8UC1);
for( int i = 0; i < 256; ++i)
{
lut.at<uchar>(0,i) = uchar(255-i);
}
cv::LUT(M, lut, result);

RGB Color Image Histogram for Contrast Stretching using OpenCV c++

I write a program to make histogram for gray image using pixel data values. I used bellow code segment to increase contrast of the image accessing pixel values. How can i apply these logic to color image to increase contrast of the image.
gr is the gray image (convert from color image) and gr_im is new image.
int x = 0;
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
x = gr.at<uchar>(i,j);
if (x < r1)
{
gr_im.at<uchar>(i, j) = cvRound((s1/double(r1))*double(x));
}
else if(x <= r2){
gr_im.at<uchar>(i, j) = cvRound((s2-s1 / double(r2-r1))*(x-r1) + s1);
}
else if (x > r2) {
gr_im.at<uchar>(i, j) = cvRound((255-s2 / double(r1))*(x - r2) + s2);
}
}
}
You can use something like code below:
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
std::vector<cv::Mat> channels;
cv::split(hsv, channels)
// now call your function to specific channel
cv::merge(channels, hsv);
cv::cvtColor(hsv, image, CV_HSV2BGR);

Embed watermark using dct on image opencv

I wants to embed watermark into an image using dct with c++ and opencv.
I split image into 8x8 block and apply dct to each block.
Now I don't know what to do next, Can anyone give me some hint or help me?
Here is my work so far.
int main() {
Mat originalImage;
originalImage = imread("image.jpg");
if( !originalImage.data )
{
std::cout<< "Error loading original image!"<<std::endl;
return -1;
}
cout << "Working on image from image.jpg" << endl;
/// Create Windows
namedWindow("Original", 1);
imshow( "Original", originalImage );
int x = 0; int y = 0;
moveWindow("Original", x, y);
imshow("Original", originalImage);
x += 100; y += 100;
int width = originalImage.size().width;
int height = originalImage.size().width;
cout << "Original image Width x Height is " << width << "x" << height << endl;
// Leave original alone, work on a copy
Mat dctImage = originalImage.clone();
// Step through the copied image with rectangles size 8x8
// For each block, split into planes, do dct, and merge back
// into the block. (This will affect the image from
// which the block is selected each time.)
for (int i = 0; i < height; i += 8)
{
for (int j = 0; j < width; j+= 8)
{
Mat block = dctImage(Rect(i, j, 8, 8));
vector<Mat> planes;
split(block, planes);
vector<Mat> outplanes(planes.size());
for (size_t k = 0; k < planes.size(); k++)
{
planes[k].convertTo(planes[k], CV_32FC1);
dct(planes[k], outplanes[k]);
outplanes[k].convertTo(outplanes[k], CV_8UC1);
}
merge(outplanes, block);
}
}
namedWindow("dctBlockImage");
moveWindow("dctBlockImage", x, y);
imshow("dctBlockImage", dctImage);
x += 100; y += 100;
waitKey();
destroyAllWindows();
return 0;
}

OpenCV counting number of pixels in a horizontal line

I wish to find number of white pixels in every row of binary image. And if that count is greater than 90, I wish to delete the entire row by changing each pixel value in that row to 0. The code that I wrote is not working. And apparently, I am getting the same binary image at output.
Please help me out in fixing the problem. BTW, am using openCV 2.0.
using namespace std;
double a = 15;
double b = 255;
Mat I1;
int main(int argv, char **argc)
{
cv: Mat I = imread("abc.bmp");
if (I.empty())
{
std::cout << "!!! Failed imread(): image not found" << std::endl;
}
threshold(I, I1, a, b, THRESH_BINARY);
int r = I.rows;
int c = I.cols;
for (int j = 0; j < r; j++)
{
int count = 0;
for (int i = 0; i < c; i++)
{
if (I1.at<uchar>(j, i) == 255)
count = count + 1;
}
if (count > 90)
{
for (int i = 0; i < c; i++)
I1.at<uchar>(j, i) = 0;
}
}
namedWindow("Display window", 0);// Create a window for display.
imshow("Display window", I1);
waitKey(0);
return 0;
}
By default imread returns 3 channel BGR image. If you want to load grayscale/binary image use cv::IMREAD_GRAYSCALE parameter:
cv::Mat I = cv::imread("abc.bmp", cv::IMREAD_GRAYSCALE);