I'm trying to get a Kinect to find a red shape and seem to be having difficulty comparing the actual RGBA values from each pixel. The code should be pretty self-explanatory. The text output file is just for me to get a quick representation of what's getting assigned to the array for future calculations.
void CColorBasics::getRGB (BYTE* bitPointer, LONG width, LONG height)
{
int red, green, blue, alpha;
int arr[640][480];
ofstream myfile;
myfile.open ("asdf.txt");
for (int i=0; i<height; i ++)
{
for (int ii=0; ii<width; ii = ii = ii+4)
{
blue = (int)bitPointer[ii];
green = (int)bitPointer[ii+1];
red = (int)bitPointer[ii+2];
alpha = (int)bitPointer[ii+3];
//calculate differences between BG and R and store result in an array
if (red > (green+blue) && red >= 150)
{
arr[i][ii] = 1;
myfile << "1";
}
else
{
arr[i][ii] = 0;
myfile << "0";
}
}
myfile << "\n";
}
myfile.close();
}
Instead of getting a pattern that resembles anything close to what the RGBA sees, I get some semi-random stuff that seems to have to do with how much red is in the picture, but certainly isn't formatted correctly. Essentially, if a pixel is "significantly red", I would like a '1' to be stored in that pixel's location in the array[][] - otherwise, store a '0' in that spot. Tips would definitely be appreciated!
Related
I've been going over my code for a few hours now and I'm not sure why this contrast algo isn't working.
Following this guide I've used the small algorithm given on the post. However I did mine using HSI color scheme because my pictures need to be in color. I have noted the changes for HSI in the post however they didn't give me a step by step on exactly how to do it. Also they're using pillow, whereas I'm using Cimg.
My code compiles and runs with no errors. But the result is a very dark image.
I was hoping for an output similar to what I get if increasing contrast using camera raw filter in photoshop. This is the a result of maxing the photoshop contrast slider:
This is the tail of the modified intensity values and the min max values:
old Intensity 0.422222
new Intensity 0.313531
old Intensity 0.437909
new Intensity 0.353135
old Intensity 0.437909
new Intensity 0.353135
old Intensity 0.436601
new Intensity 0.349835
old Intensity 0.439216
new Intensity 0.356436
old Intensity 0.443137
new Intensity 0.366337
old Intensity 0.45098
new Intensity 0.386139
old Intensity 0.458824
new Intensity 0.405941
old Intensity 0.461438
new Intensity 0.412541
min 0.298039
max 0.694118
Hope someone can help, thanks.
#include <iostream>
#include "CImg.h"
int main() {
cimg_library::CImg<float> lenaCondec("./colors/lena_condec.jpeg");
int width = lenaCondec.width();
int height = lenaCondec.height();
// enhancing contrast
float minIntensity = 1.0f;
float maxIntensity = 0.0f;
cimg_library::CImg<float> imgBuffer = lenaCondec.get_RGBtoHSI();
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++) {
const auto I = imgBuffer(col, row, 0, 2);
minIntensity = std::min((float)I, minIntensity);
maxIntensity = std::max((float)I, maxIntensity);
}
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++) {
auto I = imgBuffer(col, row, 0, 2);
const auto newIntensity = (((float)I - minIntensity) / (maxIntensity - minIntensity));
std::cout << "old Intensity " << (float)I << std::endl;
imgBuffer(col, row, 0, 2) = newIntensity;
I = imgBuffer(col, row, 0, 2);
std::cout << "new Intensity " << (float)I << std::endl;
}
std::cout << "min " << minIntensity << std::endl;
std::cout << "max " << maxIntensity << std::endl;
cimg_library::CImg<float> outputImg = imgBuffer.get_HSItoRGB();
// Debugging
outputImg.save_jpeg("./colors/output-image.jpeg");
std::getchar();
return 0;
}
I have a repo for this here. Make sure you're in the "so-question" branch.
Note: I modified line 389 of CImg.h from #include <X11/Xlib.h> -> #include "X11/Xlib.h"
The algorithm above scales the image into [0, 1] range.
Namely the pixels with the lowest values will be mapped to 0 and the pixels with highest values will be mapped to 1.
You need to apply thin on RGB image which its values are in the range [0, 1]. You need to apply it per channel.
I think there may be an issue with the built-in JPEG implementation in CImg. I found that your code works fine if you save the output file as a PNG instead of a JPEG.
Alternatively you can force CImg to use the IJPEG implementation on your Mac with:
clang++ $(pkg-config --cflags --libs libjpeg) -std=c++17 -Dcimg_use_jpeg -lm -lpthread -o "main" "main.cpp"
As a pre-requisite, you may need to install pkkconfig and jpeg with homebrew:
brew install jpeg pkgconfig
Note also that, as long as you don't want to use CImg display(), you can avoid needing to put all the paths and switches for X11 on your compilation command by changing your compilation command to this:
clang++ -Dcimg_display=0 ...
As you mentioned you might consider other ways of stretching the contrast, I thought I'd add another option where you can do it in RGB colourspace. If you find the minimum and maximum of the Red channel and stretch the reds, and then do likewise for the other channels, you will introduce a colour cast. So, an alternative is to find the minimum of all channels and maximum of all channels and then stretch the channels in concert by the same amount.
Effectively, you are stretching the RGB histogram until any of the channels hits 0 or 255. My C++ is a bit clumsy, but it looks something like this:
#include <iostream>
#include "CImg.h"
int main() {
cimg_library::CImg<unsigned char> img("lena.png");
int width = img.width();
int height = img.height();
// Find min and max RGB values for whole image
unsigned char RGBmin = 255;
unsigned char RGBmax = 0;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
const auto R = img(col, row, 0, 0);
const auto G = img(col, row, 0, 1);
const auto B = img(col, row, 0, 2);
RGBmin = std::min({R,G,B,RGBmin});
RGBmax = std::max({R,G,B,RGBmax});
}
}
std::cout << "RGBmin=" << int(RGBmin) << ", RGBmax=" << int(RGBmax) << std::endl;
// Stretch contrast equally for all channels
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
for (int chan = 0; chan <=3; chan++) {
const auto x = img(col, row, 0, chan);
const auto newVal = 255*((float)x - RGBmin) / (RGBmax - RGBmin);
img(col, row, 0, chan) = (unsigned char)newVal;
}
}
}
// Debugging
img.save("result2.png");
}
I am trying to do a simple image processing filter where the pixel values will be divided by half to reduce the intensity and I am trying to develop the hardware for the same. hence I am using vivado hls to generate the IP. As explained here https://forums.xilinx.com/t5/High-Level-Synthesis-HLS/Float-numbers-with-hls-stream/m-p/942747 to send floating numbers in a hls stream , an union needs to be used and I did the same. However, the results don't seem to be matching for the red and green components of the image whereas it is matching for the blue component of the image. It is a very simple algorithm where a pixel value will be divided by half.
I have been trying to resolve it but I am not able to see where the problem is. I have attached all the files below, can someone can help me resolve it??
////header file
#include "ap_fixed.h"
#include "hls_stream.h"
typedef union {
unsigned int i;
float r;
float g;
float b;
} conv;
typedef hls::stream <unsigned int> Stream_t;
void ftest(Stream_t& Sin,Stream_t& Sout);
////testbench
#include "stream_check_h.hpp"
int main()
{
Mat img_rev = imread("C:/Users/20181217/Desktop/images/imgs/output_fwd_v3.png");//(256x512)
Mat final_img(img_rev.rows,img_rev.cols,CV_8UC3);
Mat ref_img(img_rev.rows,img_rev.cols,CV_8UC3);
Stream_t S1,S2;
int err_r = 0;
int err_g = 0;
int err_b = 0;
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
conv c;
c.r = (float)img_rev.at<Vec3b>(i,j)[0];
c.g = (float)img_rev.at<Vec3b>(i,j)[1];
c.b = (float)img_rev.at<Vec3b>(i,j)[2];
S1 << c.i;
}
}
ftest(S1,S2);
conv c;
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
S2 >> c.i;
final_img.at<Vec3b>(i,j)[0]=(unsigned char)c.r;
final_img.at<Vec3b>(i,j)[1]=(unsigned char)c.g;
final_img.at<Vec3b>(i,j)[2]=(unsigned char)c.b;
ref_img.at<Vec3b>(i,j)[0] = (unsigned char)(((float)img_rev.at<Vec3b>(i,j)[0])/2.0);
ref_img.at<Vec3b>(i,j)[1] = (unsigned char)(((float)img_rev.at<Vec3b>(i,j)[1])/2.0);
ref_img.at<Vec3b>(i,j)[2] = (unsigned char)(((float)img_rev.at<Vec3b>(i,j)[2])/2.0);
}
}
Mat diff;
cout<<diff;
diff= abs(final_img-ref_img);
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
if((int)diff.at<Vec3b>(i,j)[0] > 0)
{
err_r++;
cout<<"expected value: "<<(int)ref_img.at<Vec3b>(i,j)[0]<<", final_value: "<<(int)final_img.at<Vec3b>(i,j)[0]<<", actual value:"<<(int)img_rev.at<Vec3b>(i,j)[0]<<endl;
}
if((int)diff.at<Vec3b>(i,j)[1] > 0)
err_g++;
if((int)diff.at<Vec3b>(i,j)[2] > 0)
err_b++;
}
}
cout<<"number of errors: "<<err_r<<", "<<err_g<<", "<<err_b;
return 0;
}
////core
#include "stream_check_h.hpp"
void ftest(Stream_t& Sin,Stream_t& Sout)
{
conv cin,cout;
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
Sin >> cin.i;
cout.r = cin.r/2.0 ;
cout.g = cin.g/2.0 ;
cout.b = cin.b/2.0 ;
Sout << cout.i;
}
}
}
when I debugged, it showed that the blue components of the pixels are matching. for one red pixel it showed me the following:
expected value: 22, final_value: 14, actual value:45
and the total errors for red, green, and blue are:
number of errors: 126773, 131072, 0
I am not able to see why it is going wrong for red and green. I posted here hoping a fresh set of eyes would help my problem.
Thanks in advance
I'm assuming you're using a 32bit-wide stream with 3 RGB pixels 8bit unsigned (CV_8U3). I believe the problem with the union type in your case is the overlapping of its three members (not just like the one float value in the example you cite). This means that by doing the division, you're actually doing it over the whole 32bit data you're receiving.
I possible workaround I quickly cam up with would be to cast the unsigned int you're getting from the stream into an ap_uint<32> type, then chop it in the R, G, B chunks (with the range() method) and divide. Finally, assemble back the result and stream it back.
unsigned int packet;
Sin >> packet;
ap_uint<32> packet_uint32 = *((ap_uint<32>*)&packet); // casting (not elegant, but works)
ap_int<8> b = packet_uint32.range(7, 0);
ap_int<8> g = packet_uint32.range(15, 8);
ap_int<8> r = packet_uint32.range(23, 16); // In case they are in the wrong bit range/order, just flip the r, g, b assignements
b /= 2;
g /= 2;
r /= 2;
packet_uint32.range(7, 0) = b;
packet_uint32.range(15, 8) = g;
packet_uint32.range(23, 16) = r;
packet = packet_uint32.to_int();
Sout << packet;
NOTE: I've reused the same variables in the code above: HLS shouldn't complain about it and come out with a good RTL anyway. In case it shouldn't, just create new ones.
I see there are similar questions to this but don't quiet answer what I am asking so here is my question.
In C++ with OpenCV I run the code I will provide below and it returns an average pixel value of 6.32. However, when I open the image and use the mean function in MATLAB it returns an average pixel intensity of approximately 6.92ish. As you can see I convert the OpenCV values to double to try to ease this issue and have found that openCV loads the image as a set of integers whereas MATLAB loads the image as decimal values that are approximately but not quite the same obviously as the integers. So my question is, being new to coding, which is correct? I'm assuming MATLAB is returning more accurate values and if that is the case I would like to know if there is a way to load the images in the same fashion to avoid the discrepancy.
Thank you, Code below
Mat img = imread("Cells2.tif");
cv::cvtColor(img, img, CV_BGR2GRAY);
cv::imshow("stuff",img);
Mat dst;
if(img.channels() == 3)
{
img.convertTo(dst, CV_64FC1);
}
else if (img.channels() == 1)
{
img.convertTo(dst, CV_64FC1);
}
cv::imshow("output",dst/255);
int NumPixels = img.total();
double avg;
double c = 0;
double std;
for(int y = 0; y < dst.cols; y++)
{
for(int x = 0; x < dst.rows; x++)
{
c+=dst.at<double>(x,y)*255;
}
}
avg = c/NumPixels;
cout << "asfa = " << c << endl;
double deviation;
double var;
double z = 0;
double q;
//for(int a = 0; a<= img.cols; a++)
for(int y = 0; y< dst.cols; y++)
{
//for(int b = 0; b<= dst.rows; b++)
for(int x = 0; x< dst.rows; x++)
{
q=dst.at<double>(x,y);
deviation = q - avg;
z = z + pow(deviation,2);
//cout << "q = " << q << endl;
}
}
var = z/(NumPixels);
std = sqrt(var);
cv::Scalar avgPixel = cv::mean(dst);
cout << "Avg Value = " << avg << endl;
cout << "StdDev = " << std << endl;
cout << "AvgPixel =" << avgPixel;
cvWaitKey(0);
return 0;
}
According to your comment, the image seems to be stored with a 16-bit depth. MATLAB loads the TIFF image as is, while by default OpenCV will load images as 8-bit. This might explain the difference in precision that you are seeing.
Use the following to open the image in OpenCV:
cv::Mat img = cv::imread("file.tif", cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR);
In MATLAB, it's simply:
img = imread('file.tif');
Next you need to be aware of the data type you are working with. In OpenCV its CV_16U, in MATLAB its uint16. Therefore you need to convert types accordingly.
For example, in MATLAB:
img2 = double(img) ./ double(intmax('uint16'));
would convert it to a double image with values in the range [0,1]
When you load the image, you must use similar methods in both environments (MATLAB and OpenCV) to avoid possible conversions which may be done by default in either environment.
You are converting the image if certain conditions are met, this can change some color values while MATLAB can choose to not convert the image but use the raw image
colors are mostly represented in hex format with popular implementations in the format of 0xAARRGGBB or 0xRRGGBBAA, so 32 bit integers will do (unsigned/signed doesn't matter, the hex value is still the same), create a 64 bit variable, add all the 32 bit variables together and then divide by the amount of pixels, this will get you a quite accurate result (for images up to 16384 by 16384 pixels (where a 32 bit value is representing the color of one pixel), if larger, then a 64 bit integer will not be enough).
long long total = 0;
long long divisor = image.width * image.height;
for(int x = 0; x < image.width; ++x)
{
for(int y = 0; x < image.height; ++x)
{
total += image.at(x,y).color;
}
}
double avg = total / divisor;
std::cout << "Average color value: " << avg << std::endl;
Not sure what difficulty you are having with mean value in Matlab versus OpenCV. If I understand your question correctly, your goal is to implement Matlab's mean(image(:)) in OpenCV. For example in Matlab you do the following:
>> image = imread('sheep.jpg')
>> avg = mean(image(:))
ans =
119.8210
Here's how you do the same in OpenCV:
Mat image = imread("sheep.jpg");
Scalar avg_pixel;
avg_pixel = mean(image);
float avg = 0;
cout << "mean pixel (RGB): " << avg_pixel << endl;
for(int i; i<image.channels(); ++i) {
avg = avg + avg_pixel[i];
}
avg = avg/image.channels();
cout << "mean, that's equivalent to mean(image(:)) in Matlab: " << avg << endl;
OpenCV console output:
mean pixel (RGB): [77.4377, 154.43, 127.596, 0]
mean, that's equivalent to mean(image(:)) in Matlab: 119.821
So the results are the same in Matlab and OpenCV.
Follow up
Found some problems in your code.
OpenCV stores data differently from Matlab. Look at this answer for a rough explanation on how to access a pixel in OpenCV. For example:
// NOT a correct way to access a pixel in CV_F32C3 type image
double pixel = image.at<double>(x,y);
//The correct way (where the pixel value is stored in a vector)
// Note that Vec3d is defined as: typedef Vec<double, 3> Vec3d;
Vec3d pixel = image.at<Vec3d>(x, y);
Another error I found
if(img.channels() == 3)
{
img.convertTo(dst, CV_64FC1); //should be CV_64FC3, instead of CV_64FC1
}
Accessing Mat elements may be confusing. I suggest getting a book on OpenCV to get started, for example this one, and read OpenCV tutorials and documentation. Hope this helps.
I'm having some troubles with pnm files (which is kinda obvious or else I wouldn't be posting here XD). Thing is, my teacher asked us to develop a simple pnm reader in binary mode then print it to the screen. I'm using libEGL (a framework avaliable here). My problem is that it works only with these two images and fails with any other one.
With birch.pnm and checkers.pnm it works, but cathedral.pnm, cotton.pnm and fish_tile.pnm it just simple enters an infinite loop or throws and error.
The images are avaliable here
My code is as follows:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include "engcomp_glib.h"
using namespace std;
struct RGB{
char red, green, blue;
};
int main(int argc, char* argv[]){
RGB **image;
RGB pixel;
//ifstream _file("..\\bin\\birch.pnm");
ifstream _file("..\\bin\\checkers.pnm");
//ifstream _file("..\\bin\\cotton.pnm");
//ifstream _file("..\\bin\\cathedral.pnm");
//ifstream _file("..\\bin\\fish_tile.pnm");
string type, maxColor;
int width, height;
if(_file){
_file >> type;
if(type != "P6")
cout << "Error! File type is not allowed." << endl;
_file >> width >> height >> maxColor;
_file.close();
egl_inicializar(width, height, true);
image = new RGB *[height];
for(int row = 0; row < height; row++)
image[row] = new RGB[width];
//Working 8D
//_file.open("..\\bin\\birch.pnm", ios::binary);
_file.open("..\\bin\\checkers.pnm", ios::binary);
//Not working D:<
//_file.open("..\\bin\\cathedral.pnm", ios::binary);
//_file.open("..\\bin\\fish_tile.pnm", ios::binary);
//_file.open("..\\bin\\cotton.pnm", ios::binary);
//imagem img; img.carregar("..\\bin\\birch.pnm");
_file.seekg(0, _file.end);
int size = _file.tellg();
int currentSize = 0, counter = 0;
char byte;
_file.seekg(0, _file.beg);
do{
_file.read(reinterpret_cast<char *> (&byte), sizeof(char));
if(byte == 10 || byte == 13)
counter++;
}while(counter < 3);
int rows = 0, columns = 0;
while(size != currentSize){
_file.read(reinterpret_cast<char *> (&pixel), sizeof(RGB));
if(rows < height && columns < width){
image[rows][columns] = pixel;
rows++;
}
else if(rows == height){
rows = 0;
columns++;
image[rows][columns] = pixel;
rows++;
}
//else if(columns >= width)
//currentSize = size;
currentSize = _file.tellg();
}
_file.close();
while(!key[SDLK_ESCAPE]){
for(int row = 0; row < height; row++)
for(int column = 0; column < width; column++)
//egl_pixel(row, column, image[row][column].red, image[row][column].green, image[row][column].blue);
egl_pixel(column, row, image[column][row].red, image[column][row].green, image[column][row].blue);
//img.desenha(0, 0);
egl_desenha_frame(false);
}
}
egl_finalizar();
return 0;
}
It doesn't make sense, as it works for two of them, should work form them all
I opened them all in a text editor and they have the header, so the problem is not there. What am I doing wrong? My colleague wrote a code that stores the pixels into an array with size [height * width] and can read almost all of the images but cathedral.pnm.
Thanks for the patience and help :)
The specs for the pnm state that the values in the header are separated by whitespace, usually newlines, but they could also be spaces or tabs (or something else I can't think of at the moment ;). The cathedral file for instance has a space as separator.
And you're reading the files top to bottom, left to right, in stead of left to right, top to bottom, as per the specs.
And if you want to be really correct, if maxColor is not less than 256, you should read shorts in stead of chars.
You can find the specs here by the way:
http://netpbm.sourceforge.net/doc/ppm.html
Good luck!
I tried the following code to print all the white pixels of this binary image without success:
Mat grayImage;
Mat rgb_Image;
int Max_value = 255;
int Global_Threshold = 155;
rgb_Image = imread( "../Tests/Object/Object.jpg", CV_LOAD_IMAGE_COLOR); // Read the file
if(! rgb_Image.data ) // Check for invalid input
{
cout << "Could not open or find the image" << endl ;
return -1;
}
//Convert to Grayscale.
cvtColor(rgb_Image, grayImage, CV_BGR2GRAY);
//Binarize the image with a fixed threshold.
threshold( grayImage, binImage, Global_Threshold, Max_Value, 0);
//Printing white pixels
for(int i = 0; i < 640; i++)
{
for(int j = 0; j < 480; j++)
{
if(255 == binImage.at<float>(i,j))
{
cout << binImage.at<float>(i,j) << endl;
}
}
}
If I print the values that are not zeroes I get strange values but not 255.
Thanks,
cvtColor will create a uchar image, and threshold will keep the data format, so your binImage is made up of uchars, and not float's.
Change
binImage.at<float>(i,j)
to
binImage.at<uchar>(i,j)
and keep in mind that comparing floats with == is usually a bad idea (even when you work with floats), because of floating-point representation errors. You may end up having a matrix full of 254.9999999999, and never meet the image(i,j)==255 condition
You need to cast it to float.
Although the image is an openCV float it's still stored in a unsigned char* block of data (so it can be easily converted) so "<<" thinks it's receiving a C string and prints the binary data until it sees a '0'