I'm looking for an efficient way of assigning values to an element of a 3-channel matrix. In particular, I need to assign HSV values to elements of a 2D cv::Mat which is initialized as follows:
cv::Mat clusterImage(height,width,CV_8UC3,cv::Scalar(0,0,0));
For this matrix, how do I set the pixel in row i and column j to an HSV value (H=59, S=255, V=255), as efficiently as possible?
My current method (complete code) is below. My fear is that splitting a matrix into channels, editing those channels and then merging them back together is not very efficient - especially since I need to do it in a loop, preferably at 30Hz and above. Does a more efficient method exist?
#include <vector>
#include <stdlib.h>
#include <iostream>
#include <opencv/cv.h>
#include <opencv/highgui.h>
using namespace std;
int main() {
int height = 480;
int width = 640;
cv::Mat clusterImage(height,width,CV_8UC3,cv::Scalar(0,0,0));
vector<cv::Mat> channels(3);
// split the channels
split(clusterImage, channels);
// modify the channels
vector<int> i;
vector<int> j;
int numberOfDots = 1000;
for (int k=0; k<numberOfDots; k++) {
i.push_back(rand() % height + 1);
j.push_back(rand() % width + 1);
}
for (int k=0; k<numberOfDots; k++) {
channels[0].at<unsigned char>(i[k],j[k]) = 59;
channels[1].at<unsigned char>(i[k],j[k]) = 255;
channels[2].at<unsigned char>(i[k],j[k]) = 255;
}
// merge channels
merge(channels, clusterImage);
// convert to RGB and draw
cv::cvtColor(clusterImage, clusterImage, CV_HSV2BGR);
imshow("test_window", clusterImage);
cv::waitKey(0);
return 0;
}
This code would be my choice:
int height = 480;
int width = 640;
cv::Mat clusterImage(height,width,CV_8UC3,cv::Scalar(0,0,0));
int numberOfDots = 1000;
int i , j;
for (int k=0; k<numberOfDots; k++)
{
i = rand() % height ; j = rand() % width ;
clusterImage.at<Vec3b>(i , j )[0] = 59;
clusterImage.at<Vec3b>(i , j )[1] = 255;
clusterImage.at<Vec3b>(i , j )[2] = 255;
}
// convert to RGB and draw
cv::cvtColor(clusterImage, clusterImage, CV_HSV2BGR);
imshow("test_window", clusterImage);
cv::waitKey(0);
Yes, you can make this a lot more efficient.
You can assign to a CV::Mat more or less directly. Assuming your system is underlying RGB, simply set up a CV::Mat of width and hight and with three or four channels (often a dummy alpha makes things a bit faster). Then look up the rgb values for HSV 59, 255, 255 - there are plenty of formulae - and set them directly. I think you can use the "at" member function but that's based on a casual glance at the CV::Mat interface.
Finally, you can get rid of the vectors i and j of the dot x, y co-cordinates, assuming you don't need them later on. Just loop on numberOfDots and generatate two temporary random numbers
Related
I want to take a gray scaled image and divide it into 32x32 sections. Each section will contain pixels and based their intensity and volume, they would be considered a 1 or a 0.
My thought is that I would name the sections like "(x,y)". For example:
Section(1,1) contains this many pixels that are within this range of intensity so this is a 1.
Does that make sense? I tried looking for the answer to this question but dividing up the image into overlaying sections doesn't seem to yield any results in the OpenCV community. Keep in mind I don't want to change the way the image looks, just divide it up into a 32x32 table with (x,y) being a "section" of the picture.
Yes you can do that. Here is the code. It is rough around the edges, but it does what you request. See comments in the code for explanations.
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
struct BradleysImage
{
int rows;
int cols;
cv::Mat data;
int intensity_threshold;
int count_threshold;
cv::Mat buff = cv::Mat(32, 32, CV_8UC1);
// When we call the operator with arguments y and x, we check
// the region(y,x). We then count the number of pixels within
// that region that are greater than some threshold. If the
// count is greater than desired number, we return 255, else 0.
int operator()(int y, int x) const
{
int j = y*32;
int i = x*32;
auto window = cv::Rect(i, j, 32, 32);
// threshold window contents
cv::threshold(data(window), buff, intensity_threshold, 1, CV_THRESH_BINARY);
int num_over_threshold = cv::countNonZero(buff);
return num_over_threshold > count_threshold ? 255 : 0;
}
};
int main() {
// Input image
cv::Mat img = cv::imread("walken.jpg", CV_8UC1);
// I resize it so that I get dimensions divisible
// by 32 and get better looking result
cv::Mat resized;
cv::resize(img, resized, cv::Size(3200, 3200));
BradleysImage b; // I had no idea how to name this so I used your nick
b.rows = resized.rows / 32;
b.cols = resized.cols / 32;
b.data = resized;
b.intensity_threshold = 128; // just some threshold
b.count_threshold = 512;
cv::Mat result(b.rows -1, b.cols-1, CV_8UC1);
for(int y = 0; y < result.rows; ++y)
for(int x = 0; x < result.cols; ++x)
result.at<uint8_t>(y, x) = b(y, x);
imwrite("walken.png", result);
return 0;
}
I used Christopher Walken's image from Wikipedia and obtained this result:
I am making a function using C++ and OpenCV that will detect the color of a pixel in an image, determine what color range it is in, and replace it with a generic color. For example, green could range from dark green to light green, the program would determine that its still green and replace it with a simple green, making the output image very simple looking. everything is set up but I'm having trouble defining the characteristics of each range and was curious if anyone knows or a formula that, given BGR values, could determine the overall color of a pixel. If not I'll have to do much experimentation and make it myself, but if something already exists that'd save time. I've done plenty of research and haven't found anything so far.
If you want to make your image simpler (i.e. with less colors), but good looking, you have a few options:
A simple approach would be to divide (integer division) by a factor N the image, and then multiply by a factor N.
Or you can divide your image into K colors, using some clustering algorithm such as kmeans showed here, or median-cut algorithm.
Original image:
Reduced colors (quantized, N = 64):
Reduced colors (clustered, K = 8):
Code Quantization:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
uchar N = 64;
img /= N;
img *= N;
imshow("Reduced", img);
waitKey();
return 0;
}
Code kmeans:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
// Cluster
int K = 8;
int n = img.rows * img.cols;
Mat data = img.reshape(1, n);
data.convertTo(data, CV_32F);
vector<int> labels;
Mat1f colors;
kmeans(data, K, labels, cv::TermCriteria(), 1, cv::KMEANS_PP_CENTERS, colors);
for (int i = 0; i < n; ++i)
{
data.at<float>(i, 0) = colors(labels[i], 0);
data.at<float>(i, 1) = colors(labels[i], 1);
data.at<float>(i, 2) = colors(labels[i], 2);
}
Mat reduced = data.reshape(3, img.rows);
reduced.convertTo(reduced, CV_8U);
imshow("Reduced", reduced);
waitKey();
return 0;
}
Yes, what you probably mean by "Overall color of a pixel" is either the "Hue" or "Saturation" of the color.
So you want a formula that transform RGB to HSV (Hue, Saturation, Value), and then you would only be interested by the Hue or Saturation values.
See: Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
EDIT: You might need to max out the saturation, and then convert it back to RGB, and inspect which value is the highest (for instance (255,0,0), or (255,0,255), etc.
If you want to access RGB value of all pixels , then below is code,
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("image_path");
for(int row = 1; row < image.rows; row++)
{
for(int col = 1; col < image.cols; col++)
{
Vec3b rgb = image.at<Vec3b>(row, col);
}
}
}
I'm trying to learn OpenCV by doing a few things on my own. In this particular case, I wanted to take the bit planes of a grayscale image. The code seems to have worked, but it only works well for the bit 7 and 6, not so much for the remaining 6, as it only shows a good result for about 1/3 of the image. I just haven't found what's wrong with it as of yet. I'd greatly appreciate some help on the matter, as I'm just doing my first codes with the libraries.
Here's what I get for the first bit:
And here is it for the 7th bit:
And here's my code:
#include <opencv2\opencv.hpp>
#include <math.h>
using namespace cv;
using namespace std;
int main( int argc, char** argv ) {
Mat m1 = imread("grayscalerose.jpg");
imshow("Original",m1);
int cols, rows, x, y;
cols = m1.cols;
rows = m1.rows;
printf("%d %d \n",m1.rows,m1.cols);
Mat out1(rows, cols, CV_8UC1, Scalar(0));
out1 = (m1/128); //Here's where I divide by either 1,2,4,8,16,32,64, or 128 to get the corresponding bit planes
for (int y = 0; y < rows; y++){
for (int x = 0; x < cols; x++){
out1.at<uchar>(y,x) = (out1.at<uchar>(y,x) % 2);
} }
out1 = out1*255;
imshow("out1",out1);
waitKey(0);
destroyWindow( "out1" );
}
Thanks in advance. I hope my explanation wasn't too messy.
First let's read the image in as grayscale only. (As mentioned by user3896254).
Then, let's prepare a mask image, where only the least significant bit is set -- i.e. all the values are 1.
Then the algorithm is simple. Let's avoid per-pixel manipulation (the two nested for loops), and try to take advantage of the optimized operations provided by OpenCV.
For each bit (0..7):
Mask out the lowest order bit in the work image.
Scale the masked image by 255 to make it black/white.
Store the output.
Divide values in work image by 2 -- i.e. shift all bits by 1 position to the right.
Code:
#include <opencv2\opencv.hpp>
#include <cstdint>
int main(int argc, char** argv)
{
cv::Mat input_img(cv::imread("peppers.png", 0));
int32_t rows(input_img.rows), cols(input_img.cols);
cv::Mat bit_mask(cv::Mat::ones(rows, cols, CV_8UC1));
cv::Mat work_img(input_img.clone());
std::string file_name("peppers_bit0.png");
for (uint32_t i(0); i < 8; ++i) {
cv::Mat out;
cv::bitwise_and(work_img, bit_mask, out);
out *= 255;
cv::imwrite(file_name, out);
work_img = work_img / 2;
file_name[11] += 1;
}
}
We can develop even shorter (and probably faster) version using a single matrix expression.
We can calculate the appropriate divisor using the expression (1<<i). We divide every element by this value to shift the bits, mask each element by ANDing it with 1, and then scale all the elements by 255:
#include <opencv2\opencv.hpp>
#include <cstdint>
int main(int argc, char** argv)
{
cv::Mat input_img(cv::imread("peppers.png", 0));
std::string file_name("peppers_bit0.png");
for (uint32_t i(0); i < 8; ++i) {
cv::Mat out(((input_img / (1<<i)) & 1) * 255);
cv::imwrite(file_name, out);
file_name[11] += 1;
}
}
Sample run
Input image:
Bit 0:
Bit 1:
Bit 2:
Bit 3:
Bit 4:
Bit 5:
Bit 6:
Bit 7:
When you divide 15 (0x00001111) by 2 (0x00000010) you get 7 (0x00000111), which is not what you expect. You can check if a bit is set like: 15 & 2, which produces 0 if second bit is not set, else a value greater then 0. The same applies for other values.
Try the following code. Note that:
you need to load the image as grayscale (using IMREAD_GRAYSCALE in imread)
you can directly put values either 0 or 255 when you select the bit
Code:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat m1 = imread("path_to_image", IMREAD_GRAYSCALE);
imshow("Original", m1);
int cols, rows, x, y;
cols = m1.cols;
rows = m1.rows;
printf("%d %d \n", m1.rows, m1.cols);
Mat out1(rows, cols, CV_8UC1, Scalar(0));
for (int y = 0; y < rows; y++){
for (int x = 0; x < cols; x++){
out1.at<uchar>(y, x) = (m1.at<uchar>(y, x) & uchar(64)) ? uchar(255) : uchar(0); //Here's where I AND by either 1,2,4,8,16,32,64, or 128 to get the corresponding bit planes
}
}
imshow("out1", out1);
waitKey(0);
destroyWindow("out1");
return 0;
}
By default, cv::imread reads the image as BGR matrix, but you index the matrix as if it was one-channeled.
Just change the reading line to Mat m1 = imread("grayscalerose.jpg", 0); and it will work fine.
Mat Out (In / (1<<i)), the division of it will generate an integer value that equals to "round operation", let's say if Mat Out (6/5) will be 2. But, in bit-slicing, it uses floor operation instead of round. Thus, for Mat Out (6/5) it should be 1 instead of 2. For some cases, the result will be quite similar. But, in other cases, it can be really different, especially for bit-plane near to MSB (most significant bits). CMIIW.
I am making a function using C++ and OpenCV that will detect the color of a pixel in an image, determine what color range it is in, and replace it with a generic color. For example, green could range from dark green to light green, the program would determine that its still green and replace it with a simple green, making the output image very simple looking. everything is set up but I'm having trouble defining the characteristics of each range and was curious if anyone knows or a formula that, given BGR values, could determine the overall color of a pixel. If not I'll have to do much experimentation and make it myself, but if something already exists that'd save time. I've done plenty of research and haven't found anything so far.
If you want to make your image simpler (i.e. with less colors), but good looking, you have a few options:
A simple approach would be to divide (integer division) by a factor N the image, and then multiply by a factor N.
Or you can divide your image into K colors, using some clustering algorithm such as kmeans showed here, or median-cut algorithm.
Original image:
Reduced colors (quantized, N = 64):
Reduced colors (clustered, K = 8):
Code Quantization:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
uchar N = 64;
img /= N;
img *= N;
imshow("Reduced", img);
waitKey();
return 0;
}
Code kmeans:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
// Cluster
int K = 8;
int n = img.rows * img.cols;
Mat data = img.reshape(1, n);
data.convertTo(data, CV_32F);
vector<int> labels;
Mat1f colors;
kmeans(data, K, labels, cv::TermCriteria(), 1, cv::KMEANS_PP_CENTERS, colors);
for (int i = 0; i < n; ++i)
{
data.at<float>(i, 0) = colors(labels[i], 0);
data.at<float>(i, 1) = colors(labels[i], 1);
data.at<float>(i, 2) = colors(labels[i], 2);
}
Mat reduced = data.reshape(3, img.rows);
reduced.convertTo(reduced, CV_8U);
imshow("Reduced", reduced);
waitKey();
return 0;
}
Yes, what you probably mean by "Overall color of a pixel" is either the "Hue" or "Saturation" of the color.
So you want a formula that transform RGB to HSV (Hue, Saturation, Value), and then you would only be interested by the Hue or Saturation values.
See: Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
EDIT: You might need to max out the saturation, and then convert it back to RGB, and inspect which value is the highest (for instance (255,0,0), or (255,0,255), etc.
If you want to access RGB value of all pixels , then below is code,
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("image_path");
for(int row = 1; row < image.rows; row++)
{
for(int col = 1; col < image.cols; col++)
{
Vec3b rgb = image.at<Vec3b>(row, col);
}
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I m trying to find the maximum and minimum of RGB values of an image.
the flow in which i was planning to go is:
load the image.
after loading the image, create a 15x15 cell around the cell to be tested
find the max of RGB of the test cell and store it in an array.
then print the image with the value of max RGB, According to me the image should be a DARK image. The max of RGB corresponds to the dark portion of the image
The problem here is i m new to image processing, opencv.
i dont know how to implement these things which i mentioned abovei have attached a picture related to my doubt
Here is code, i have just read image and got some details of image
#include "iostream"
#include "string.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "opencv2/opencv.hpp"
float lambda=0.0001; //lambda
double _w=0.95; //w
int height=0; //image Height
int width=0; //image Width
int size=0; //total number of pixels
int blockdim = 32;
char img_name[100]="1.png";
Mat read_image()
{
Mat img = imread(img_name);
height = img.rows;
width = img.cols;
size = img.rows*img.cols;
Mat real_img(img.rows,img.cols,CV_32FC3);
img.convertTo(real_img,CV_32FC3);
return real_img;
}
//Main Function
int main(int argc, char * argv[])
{
Mat img = read_image();
/*****************************************************************/
// Till here i have done my code. i.e. Read my image and get all details about the image
// Now i'm not getting the logic to find the Min/Max of RGB values in an image for
// 15x15 cell
return 0;
}
Finally i want to implement this on GPU, i have learnt few things about GPU,CUDA and played on GPU. Now i wanna do some stuff related to image processing on GPU(CUDA)
I want to compute the extent of haze of an image for each block. This is done by finding the dark channel value that is used to reflect the extent of haze. This concept is from Kaiming He's paper on a Single Image Haze Removal using Dark Channel Prior.
The dark channel value for each block is defined as follows:
where I^c (x',y') denotes the intensity at a pixel location (x',y') in color channel c (one of Red, Green, or Blue color channel), and omega(x,y) denotes the neighborhood of the pixel location (x',y').
since i m new to Image processing and open cv, i'm not sure how to translate this equation
I already implemented this some time ago, and below is the code snippet. Probably could be further optimized, and you should add cuda support by yourself, but this could be a good starting point.
The main steps are:
Load a BGR image
Compute a single channel matrix with the minimum of B,G,R (minValue3b).
Compute the minimum in a patchSize x patchSize neighborhood (minFilter).
NOTES
You need to find the minimum value, not the maximum.
To avoid border issues while searching the minimum in the neighborhood, you can simply add a border big enough around the image, with the maximum allowed value (i.e. 255). You can use copyMakeBorder for this.
Input:
DCP:
Code:
#include <opencv2/opencv.hpp>
using namespace cv;
void minFilter(const Mat1b& src, Mat1b& dst, int radius)
{
Mat1b padded;
copyMakeBorder(src, padded, radius, radius, radius, radius, BORDER_CONSTANT, Scalar(255));
int rr = src.rows;
int cc = src.cols;
dst = Mat1b(rr, cc, uchar(0));
for (int c = 0; c < cc; ++c)
{
for (int r = 0; r < rr; ++r)
{
uchar lowest = 255;
for (int i = -radius; i <= radius; ++i)
{
for (int j = -radius; j <= radius; ++j)
{
uchar val = padded(radius + r + i, radius + c + j);
if (val < lowest) lowest = val;
}
}
dst(r, c) = lowest;
}
}
}
void minValue3b(const Mat3b& src, Mat1b& dst)
{
int rr = src.rows;
int cc = src.cols;
dst = Mat1b(rr, cc, uchar(0));
for (int c = 0; c<cc; ++c)
{
for (int r = 0; r<rr; ++r)
{
const Vec3b& v = src(r, c);
uchar lowest = v[0];
if (v[1] < lowest) lowest = v[1];
if (v[2] < lowest) lowest = v[2];
dst(r, c) = lowest;
}
}
}
void DarkChannel(const Mat3b& img, Mat1b& dark, int patchSize)
{
int radius = patchSize / 2;
Mat1b low;
minValue3b(img, low);
minFilter(low, dark, radius);
}
int main()
{
// Load the image
Mat3b img = imread("path_to_image");
// Compute DCP
Mat1b dark;
DarkChannel(img, dark, 15);
// Show results
imshow("Img", img);
imshow("Dark", dark);
waitKey();
return 0;
}