Setting RGB values of pixels in a certain image in openCV - c++

I am trying to set RGB values of some pixels in a binary image. But whenever the coordinate is more than (89, 89) its giving me an assertion error! My image resolution is okay because I am accessing RGB values from (150, 150) coordinate. If the coordinate is (89, 89) or less it works fine. My Code:
cv::Mat img_gray, img_bw;
//read an image
cv::Mat3b img_bgr = cv::imread("test.jpg");
cv::imshow("Original Image", img_bgr);
//conversion to binary from color
cv::cvtColor(img_bgr, img_gray,CV_RGB2GRAY);
cv::threshold(img_gray, img_bw, 75.0, 255.0, THRESH_BINARY);
//accessing BGR of position (150, 150) from a color image
std::cout<<"Pixel at position (x, y) : ("<<150<<", "<<150<<") ="<<img_bgr(150,150)<<std::endl;
//Setting BGR of position (150, 150) in binary image
img_bw.at<Vec3b>(150, 150)[0] = 255;
img_bw.at<Vec3b>(150, 150)[1] = 255;
img_bw.at<Vec3b>(150, 150)[2] = 255;
std::cout<<"Pixel at position (x, y) : ("<<150<<", "<<150<<") ="<<img_bw.at<Vec3b>(150, 150)<<std::endl;
Here if I put 89 instead of 150 in the "Setting BGR" section then it works. Otherwise the full error is:
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 < (unsigned)size.p[0] && (unsigned)(i1*DataType<_Tp>::channels) < (unsigned)(size.p1*channels()) && ((((sizeof(size_t)<<28)|0x8442211) >> ((DataType<_Tp>::depth) & ((1 << 3) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file e:\opencv\opencv\build\include\opencv2\core\mat.hpp, line 538
So is this any type of memory space error?
Thanks in advance for the helps! :)
UPDATE: I've tried it this way! But the output is blank now.
cv::Mat img_gray, img_bw;
//read an image
cv::Mat3b img_bgr = cv::imread("test.jpg");
cv::imshow("Original Image", img_bgr);
//conversion to binary from color
cv::cvtColor(img_bgr, img_gray,CV_RGB2GRAY);
cv::threshold(img_gray, img_bw, 75.0, 255.0, THRESH_BINARY);
//accessing BGR of position (150, 150) from a color image
std::cout<<"Pixel at position (x, y) : ("<<150<<", "<<150<<") ="<<img_bgr(150,150)<<std::endl;
//Setting BGR of position (150, 150) in binary image
img_bw.at<uchar>(150, 150) = 255;
std::cout<<"Pixel at position (x, y) : ("<<150<<", "<<150<<") ="<<img_bw.at<uchar>(150, 150)<<std::endl;
My test image is here
And the output is here

Okay I have the answer now thanks to beaker's hint in the comment :) I'm putting the solution for others help!
I just need to define the output as an integer. So the last cout will be like
std::cout<<"Pixel at position (x, y) : ("<<150<<", "<<150<<") ="<<(int)img_bw.at<uchar>(150, 150)<<std::endl;

Related

I'm getting an error from the implementation of my region of interest

Error message:
(-215) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols &&
0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in
function cv::Mat::Mat
This is my code
Rect eye_rec(200, 300, 168, 168);
Point hand_pos(100, 100);
Mat des, mask = (cv::Mat::zeros(hand.size(), CV_8UC1));
mask(eye_rec).setTo(255);
seamlessClone(eye,hand, mask,hand_pos,des,NORMAL_CLONE);
imshow("clone", des);
waitKey(0);
i cant really understand the error message though..
Your error code generally means, that ROI you want to crop is out of the bounds of the source matrix - e.g. source matrix is of size 480x480 and you want to crop out ROI of size 300x300 from position (200, 200), where 300+200 > 480.
According to docs
src – Input 8-bit 3-channel image.
dst – Input 8-bit 3-channel image.
mask – Input 8-bit 1 or 3-channel image.
result – Output image with the same size and type as dst.
src, dst and result should be of type CV_8UC3 - three channel images, while you are passing just one channel images CV_8UC1, which most likely cause the error here.
The solution is to use 3-channel (color) images or different operation accepting 1-channel images.
hand.convertTo(hand, CV_8UC3);
eye.convertTo(eye, CV_8UC3);
Point hand_pos(hand.cols/2,hand.rows/2); //this code should put the eye image in the middle of the hand image
Mat des, mask = (cv::Mat::zeros(eye.size(), CV_8UC3));
des.convertTo(des, CV_8UC3);
mask = 255 * Mat::ones(eye.rows, eye.cols, eye.depth()); // creating a mask of all white from the eye image
seamlessClone(eye,hand, mask,hand_pos,des,NORMAL_CLONE);
imshow("normalclone", des); waitKey(0);
seamlessClone(eye,hand,mask,hand_pos,des, MIXED_CLONE);
imshow("mixclone",des); waitKey(0)
waitKey(0);
This change helped me, hope it helps others too, thanks #Filip Kočica

Change all white pixels of image to transparent in OpenCV C++

I have this image in OpenCV imgColorPanel = imread("newGUI.png", CV_LOAD_IMAGE_COLOR);:
When I load it in with grey scale imgColorPanel = imread("newGUI.png", CV_LOAD_IMAGE_GRAYSCALE); it looks like this:
However I want to remove the white background or make it transparent (only it's white pixels), to be looking like this:
How to do it in C++ OpenCV ?
You can convert the input image to BGRA channel (color image with alpha channel) and then modify each pixel that is white to set the alpha value to zero.
See this code:
// load as color image BGR
cv::Mat input = cv::imread("C:/StackOverflow/Input/transparentWhite.png");
cv::Mat input_bgra;
cv::cvtColor(input, input_bgra, CV_BGR2BGRA);
// find all white pixel and set alpha value to zero:
for (int y = 0; y < input_bgra.rows; ++y)
for (int x = 0; x < input_bgra.cols; ++x)
{
cv::Vec4b & pixel = input_bgra.at<cv::Vec4b>(y, x);
// if pixel is white
if (pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255)
{
// set alpha to zero:
pixel[3] = 0;
}
}
// save as .png file (which supports alpha channels/transparency)
cv::imwrite("C:/StackOverflow/Output/transparentWhite.png", input_bgra);
This will save your image with transparency.
The result image opened with GIMP looks like:
As you can see, some "white regions" are not transparent, this means your those pixel weren't perfectly white in the input image.
Instead you can try
// if pixel is white
int thres = 245; // where thres is some value smaller but near to 255.
if (pixel[0] >= thres&& pixel[1] >= thres && pixel[2] >= thres)

Getting back to the original coordinates from a warped Image

I have four corners extracted from a image,
std::vector<cv::Point2f> ecken;
ecken.push_back(corners[0]);
ecken.push_back(corners[size.height-1]);
ecken.push_back(corners[size.area()-size.height-1]);
ecken.push_back(corners[size.area()-1]);
they are warped in to a second image:
quad_pts.push_back(cv::Point2f(0, 0));
quad_pts.push_back(cv::Point2f(quad.cols, 0));
quad_pts.push_back(cv::Point2f(quad.cols, quad.rows));
quad_pts.push_back(cv::Point2f(0, quad.rows));
// Get transformation matrix
cv::Mat transmtx = cv::getPerspectiveTransform(ecken, quad_pts);
cv::warpPerspective(src, quad, transmtx, quad.size(),1);
I want to go back to the original image from the result that I get in quad, these what I've tried:
cv::Mat trans = cv::getPerspectiveTransform(quad_pts,ecken );
cv::perspectiveTransform(quad,test,trans); /// I'm not sure that this correct and the program crashes here
and here the error message in the console:
OpenCV Error: Assertion failed (scn + 1 == m.cols && (depth == CV_32F || depth =
= CV_64F)) in unknown function, file ..\..\..\opencv\modules\core\src\matmul.cpp
, line 1926
and it didn't work !!
any idea ??
Your two Mat objects are of different depth. Check by printing Mat.depth() that both have the same image type. It could be that one of them is grayscale and the other colour.

How to determine a region of interest and then crop an image using OpenCV

I asked a similar question here but that is focused more on tesseract.
I have a sample image as below. I would like to make the white square my Region of Interest and then crop out that part (square) and create a new image with it. I will be working with different images so the square won't always be at the same location in all images. So I will need to somehow detect the edges of the square.
What are some pre-processing methods I can perform to achieve the result?
Using your test image I was able to remove all the noises with a simple erosion operation.
After that, a simple iteration on the Mat to find for the corner pixels is trivial, and I talked about that on this answer. For testing purposes we can draw green lines between those points to display the area we are interested at in the original image:
At the end, I set the ROI in the original image and crop out that part.
The final result is displayed on the image below:
I wrote a sample code that performs this task using the C++ interface of OpenCV. I'm confident in your skills to translate this code to Python. If you can't do it, forget the code and stick with the roadmap I shared on this answer.
#include <cv.h>
#include <highgui.h>
int main(int argc, char* argv[])
{
cv::Mat img = cv::imread(argv[1]);
std::cout << "Original image size: " << img.size() << std::endl;
// Convert RGB Mat to GRAY
cv::Mat gray;
cv::cvtColor(img, gray, CV_BGR2GRAY);
std::cout << "Gray image size: " << gray.size() << std::endl;
// Erode image to remove unwanted noises
int erosion_size = 5;
cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size) );
cv::erode(gray, gray, element);
// Scan the image searching for points and store them in a vector
std::vector<cv::Point> points;
cv::Mat_<uchar>::iterator it = gray.begin<uchar>();
cv::Mat_<uchar>::iterator end = gray.end<uchar>();
for (; it != end; it++)
{
if (*it)
points.push_back(it.pos());
}
// From the points, figure out the size of the ROI
int left, right, top, bottom;
for (int i = 0; i < points.size(); i++)
{
if (i == 0) // initialize corner values
{
left = right = points[i].x;
top = bottom = points[i].y;
}
if (points[i].x < left)
left = points[i].x;
if (points[i].x > right)
right = points[i].x;
if (points[i].y < top)
top = points[i].y;
if (points[i].y > bottom)
bottom = points[i].y;
}
std::vector<cv::Point> box_points;
box_points.push_back(cv::Point(left, top));
box_points.push_back(cv::Point(left, bottom));
box_points.push_back(cv::Point(right, bottom));
box_points.push_back(cv::Point(right, top));
// Compute minimal bounding box for the ROI
// Note: for some unknown reason, width/height of the box are switched.
cv::RotatedRect box = cv::minAreaRect(cv::Mat(box_points));
std::cout << "box w:" << box.size.width << " h:" << box.size.height << std::endl;
// Draw bounding box in the original image (debugging purposes)
//cv::Point2f vertices[4];
//box.points(vertices);
//for (int i = 0; i < 4; ++i)
//{
// cv::line(img, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
//}
//cv::imshow("Original", img);
//cv::waitKey(0);
// Set the ROI to the area defined by the box
// Note: because the width/height of the box are switched,
// they were switched manually in the code below:
cv::Rect roi;
roi.x = box.center.x - (box.size.height / 2);
roi.y = box.center.y - (box.size.width / 2);
roi.width = box.size.height;
roi.height = box.size.width;
std::cout << "roi # " << roi.x << "," << roi.y << " " << roi.width << "x" << roi.height << std::endl;
// Crop the original image to the defined ROI
cv::Mat crop = img(roi);
// Display cropped ROI
cv::imshow("Cropped ROI", crop);
cv::waitKey(0);
return 0;
}
Seeing that the text is the only large blob, and everything else is barely larger than a pixel, a simple morphological opening should suffice
You can do this in opencv
or with imagemagic
Afterwards the white rectangle should be the only thing left in the image. You can find it with opencvs findcontours, with the CvBlobs library for opencv or with the imagemagick -crop function
Here is your image with 2 steps of erosion followed by 2 steps of dilation applied:
You can simply plug this image into the opencv findContours function as in the Squares tutorial example to get the position
input
#objective:
#1)compress large images to less than 1000x1000
#2)identify region of interests
#3)save rois in top to bottom order
import cv2
import os
def get_contour_precedence(contour, cols):
tolerance_factor = 10
origin = cv2.boundingRect(contour)
return ((origin[1] // tolerance_factor) * tolerance_factor) * cols + origin[0]
# Load image, grayscale, Gaussian blur, adaptive threshold
image = cv2.imread('./images/sample_0.jpg')
#compress the image if image size is >than 1000x1000
height, width, color = image.shape #unpacking tuple (height, width, colour) returned by image.shape
while(width > 1000):
height = height/2
width = width/2
print(int(height), int(width))
height = int(height)
width = int(width)
image = cv2.resize(image, (width, height))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)
# Dilate to combine adjacent text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
ret,thresh3 = cv2.threshold(image,127,255,cv2.THRESH_BINARY_INV)
dilate = cv2.dilate(thresh, kernel, iterations=4)
# Find contours, highlight text areas, and extract ROIs
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#cnts = cv2.findContours(thresh3, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
#ORDER CONTOURS top to bottom
cnts.sort(key=lambda x:get_contour_precedence(x, image.shape[1]))
#delete previous roi images in folder roi to avoid
dir = './roi/'
for f in os.listdir(dir):
os.remove(os.path.join(dir, f))
ROI_number = 0
for c in cnts:
area = cv2.contourArea(c)
if area > 10000:
x,y,w,h = cv2.boundingRect(c)
#cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
cv2.rectangle(image, (x, y), (x + w, y + h), (100,100,100), 1)
#use below code to write roi when results are good
ROI = image[y:y+h, x:x+w]
cv2.imwrite('roi/ROI_{}.jpg'.format(ROI_number), ROI)
ROI_number += 1
cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()
roi detection
output

Detecting black/gray elements in OpenCV

You guys know how to detect/return the points of all black/grayish element in an image?
If possible, please include any tutorial codes for me.
Edit: I've made a thresholded image from the source "img". and I'm trying to make all the colored pixel into white.
for(x=0; x<img->width; x++) {
for(y=0;y<img->height; y++) {
uchar* temp_ptr = &((uchar*)(img_result_threshold->imageData + img_result_threshold->widthStep*y))[x];
s = cvGet2D(img_hsv, y, x);
if(s.val[1] >= 100 && s.val[2] >= 100) {
temp_ptr[0]=255; //White to greater of threshold
printf("Point(%d, %d) = (%.1f, %.1f, %.1f)\n", x, y, s.val[0], s.val[1], s.val[2]);
} else {
temp_ptr[0]=0; //Black other
}
}
}
Assuming the input image is in 24 bit format i.e. R G B then a pixel is greyscale if all three values (R G and B) are the same.
So loop through the image, check if the current pixel's R, G and B elements have the same value and if they don't then set the pixel to white.
You will then be left with an image with just the greyscale pixels.
If you want just dark grey pixels, then when you check to see if RGB values are the same you can do a second check to see if the value is less than say 127 (or whatever you want the threshold to be).
Convert the color image into gray image first by the following, provided your image is RGB
cvtColor(im_rgb,im_gray,CV_RGB2GRAY);
Convert the image into binary using your threshold, say 127
cvThreshold(im_gray, im_bw, 127, 255, CV_THRESH_BINARY);