Make 32x32 sections on an image in C++ OpenCV? - c++

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:

Related

How to set zeros values in a part of cv::Mat

I have a cv::Mat of a RGB image as
cv::Mat cv_img
I want to set zeros value for cv_img at some positions. For example from the bottom to the half location of the image will be filled by zero values. How can I do it in c++ and opencv? Thanks all.
I have searched a setTo function and mask may be a candidate solution, but how to define a binary mask is difficult for me.
cv_img.setTo(Scalar(0,0,0), mask);
You can achieve it by setting the pixels with a desired value. Just define the intervals of roi(region of interest.
Here is a simple code to guide:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("/ur/img/dir/img.jpg");
for(int i=img.rows/2; i<img.rows;i++)
{
for(int j=0; j<img.cols; j++)
{
img.at<Vec3b>(Point(j,i))[0] = 0;
img.at<Vec3b>(Point(j,i))[1] = 0;
img.at<Vec3b>(Point(j,i))[2] = 0;
}
}
imshow("Result",img);
waitKey(0);
return 0;
}
You can try this:
int w = cv_img.cols;
int h = cv_img.rows;
cv::Rect rectZero(0, h/2, w, h/2);
cv_img(rectZero) = cv::Scalar(0,0,0);

Assign HSV values to specific pixels?

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

Bit planes of a 1-plane image in OpenCV only work for 1/3 of the image

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.

How to find haze extent of an image with OpenCV on CUDA? [closed]

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;
}

OpenCV: color extraction based on Gaussian mixture model

I am trying to use opencv EM algorithm to do color extraction.I am using the following code based on example in opencv documentation:
cv::Mat capturedFrame ( height, width, CV_8UC3 );
int i, j;
int nsamples = 1000;
cv::Mat samples ( nsamples, 2, CV_32FC1 );
cv::Mat labels;
cv::Mat img = cv::Mat::zeros ( height, height, CV_8UC3 );
img = capturedFrame;
cv::Mat sample ( 1, 2, CV_32FC1 );
CvEM em_model;
CvEMParams params;
samples = samples.reshape ( 2, 0 );
for ( i = 0; i < N; i++ )
{
//from the training samples
cv::Mat samples_part = samples.rowRange ( i*nsamples/N, (i+1)*nsamples/N);
cv::Scalar mean (((i%N)+1)*img.rows/(N1+1),((i/N1)+1)*img.rows/(N1+1));
cv::Scalar sigma (30,30);
cv::randn(samples_part,mean,sigma);
}
samples = samples.reshape ( 1, 0 );
//initialize model parameters
params.covs = NULL;
params.means = NULL;
params.weights = NULL;
params.probs = NULL;
params.nclusters = N;
params.cov_mat_type = CvEM::COV_MAT_SPHERICAL;
params.start_step = CvEM::START_AUTO_STEP;
params.term_crit.max_iter = 300;
params.term_crit.epsilon = 0.1;
params.term_crit.type = CV_TERMCRIT_ITER|CV_TERMCRIT_EPS;
//cluster the data
em_model.train ( samples, Mat(), params, &labels );
cv::Mat probs;
probs = em_model.getProbs();
cv::Mat weights;
weights = em_model.getWeights();
cv::Mat modelIndex = cv::Mat::zeros ( img.rows, img.cols, CV_8UC3 );
for ( i = 0; i < img.rows; i ++ )
{
for ( j = 0; j < img.cols; j ++ )
{
sample.at<float>(0) = (float)j;
sample.at<float>(1) = (float)i;
int response = cvRound ( em_model.predict ( sample ) );
modelIndex.data [ modelIndex.cols*i + j] = response;
}
}
My question here is:
Firstly, I want to extract each model, here totally five, then store those corresponding pixel values in five different matrix. In this case, I could have five different colors seperately. Here I only obtained their indexes, is there any way to achieve their corresponding colors here? To make it easy, I can start from finding the dominant color based on these five GMMs.
Secondly, here my sample datapoints are "100", and it takes about nearly 3 seconds for them. But I want to do all these things in no more than 30 milliseconds. I know OpenCV background extraction, which is using GMM, performs really fast, below 20ms, that means, there must be a way for me to do all these within 30 ms for all 600x800=480000 pixels. I found predict function is the most time consuming one.
First Question:
In order to do color extraction you first need to train the EM with your input pixels. After that you simply loop over all the input pixels again and use predict() to classify each of them. I've attached a small example that utilizes EM for foreground/background separation based on colors. It shows you how to extract the dominant color (mean) of each gaussian and how to access the original pixel color.
#include <opencv2/opencv.hpp>
int main(int argc, char** argv) {
cv::Mat source = cv::imread("test.jpg");
//ouput images
cv::Mat meanImg(source.rows, source.cols, CV_32FC3);
cv::Mat fgImg(source.rows, source.cols, CV_8UC3);
cv::Mat bgImg(source.rows, source.cols, CV_8UC3);
//convert the input image to float
cv::Mat floatSource;
source.convertTo(floatSource, CV_32F);
//now convert the float image to column vector
cv::Mat samples(source.rows * source.cols, 3, CV_32FC1);
int idx = 0;
for (int y = 0; y < source.rows; y++) {
cv::Vec3f* row = floatSource.ptr<cv::Vec3f > (y);
for (int x = 0; x < source.cols; x++) {
samples.at<cv::Vec3f > (idx++, 0) = row[x];
}
}
//we need just 2 clusters
cv::EMParams params(2);
cv::ExpectationMaximization em(samples, cv::Mat(), params);
//the two dominating colors
cv::Mat means = em.getMeans();
//the weights of the two dominant colors
cv::Mat weights = em.getWeights();
//we define the foreground as the dominant color with the largest weight
const int fgId = weights.at<float>(0) > weights.at<float>(1) ? 0 : 1;
//now classify each of the source pixels
idx = 0;
for (int y = 0; y < source.rows; y++) {
for (int x = 0; x < source.cols; x++) {
//classify
const int result = cvRound(em.predict(samples.row(idx++), NULL));
//get the according mean (dominant color)
const double* ps = means.ptr<double>(result, 0);
//set the according mean value to the mean image
float* pd = meanImg.ptr<float>(y, x);
//float images need to be in [0..1] range
pd[0] = ps[0] / 255.0;
pd[1] = ps[1] / 255.0;
pd[2] = ps[2] / 255.0;
//set either foreground or background
if (result == fgId) {
fgImg.at<cv::Point3_<uchar> >(y, x, 0) = source.at<cv::Point3_<uchar> >(y, x, 0);
} else {
bgImg.at<cv::Point3_<uchar> >(y, x, 0) = source.at<cv::Point3_<uchar> >(y, x, 0);
}
}
}
cv::imshow("Means", meanImg);
cv::imshow("Foreground", fgImg);
cv::imshow("Background", bgImg);
cv::waitKey(0);
return 0;
}
I've tested the code with the following image and it performs quite good.
Second Question:
I've noticed that the maximum number of clusters has a huge impact on the performance. So it's better to set this to a very conservative value instead of leaving it empty or setting it to the number of samples like in your example. Furthermore the documentation mentions an iterative procedure to repeatedly optimize the model with less-constrained parameters. Maybe this gives you some speed-up. To read more please have a look at the docs inside the sample code that is provided for train() here.