I would like to know how to access a pixel value and floodfill it. On the program execution, it finds the white and black pixels, but when applying the floodfill function, the final result is a -all gray- image. What i am trying to do is this:
int best_resut = 0;
Point best = (0,0);
for(int i = 0; i < img_bin.rows; i++)
{
for(int j = 0; j < img_bin.cols; j++)
{
Vec3b intensity = img_bin.at<uchar>(i, j);
uchar color = intensity.val[0];
printf("Looking (%d,%d) Value %d\n", i, j, color);
if(color==255)
{
printf("White Pixel\n");
Point sp = (i, j);
int current_result = floodFill(img_bin, sp, 128);
if (current_result > best_result)
{
best_result = current_result;
best = Point (i,j);
floodFill(img_bin,sp, 255);
}
else
{
floodFill(img_bin, sp, 255);
}
}
}
}
namedWindow("final",CV_WINDOW_AUTOSIZE);
imshow("final", img_bin);
Assuming your image is a 3 channel color image
Vec3b intensity = img_bin.at<uchar>(i, j);
should be
Vec3b intensity = img_bin.at<Vec3b>(i, j);
And since Point takes (x, y) arguments, it should be Point sp = (j, i);, not Point sp = (i, j);
Correct code:
int best_result = 0;
cv::Point best = (0,0);
cv::Vec3b white(255, 255, 255);
for(int i = 0; i < img_bin.rows; i++)
{
cv::Vec3b* img_row = img_bin.ptr<cv::Vec3b>(i);
for(int j = 0; j < img_bin.cols; j++)
{
cv::Vec3b pixel = img_row[j];
printf("Looking (%d,%d) Value %d %d %d\n", j, i, pixel[2], pixel[1], pixel[0]);
if(pixel == white)
{
printf("White Pixel\n");
cv::Point sp(j, i);
int current_result = cv::floodFill(img_bin, sp, cv::Scalar(128, 128, 128));
if (current_result > best_result)
{
best_result = current_result;
best = sp;
}
cv::floodFill(img_bin, sp, cv::Scalar(255, 255, 255));
}
}
}
cv::namedWindow("final",CV_WINDOW_AUTOSIZE);
cv::imshow("final", img_bin);
cv::waitKey(0);
Related
I have the following code in Processing that will produce a grid of randomly selected tiles from loaded files:
static int img_count = 6;
PImage[] img;
void setup() {
size(1200, 800);
img = new PImage[img_count];
for (int i = 0; i < img_count; i++) {
img[i] = loadImage("./1x/Artboard " + (i+1) + ".png");
}
}
void draw() {
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
int rand_index = int(random(img_count));
image(img[rand_index], 100 * i, 100 * j, 100, 100 );
}
}
}
By itself, it almost does what I want:
But I need that every tile be randomly rotated as well, so I tried this:
void draw() {
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
float r = int(random(4)) * HALF_PI; // I added this
rotate(r); // I added this
int rand_index= int(random(img_count));
image(img[rand_index], 100 * i, 100 * j, 100, 100 );
}
}
}
This second code doesn't act as I intended, as rotate() will rotate the entire image, including tiles that were already rendered. I couldn't find an appropriate way to rotate a tile the way I want, is there any way to rotate the tile before placing it?
You will probably need to translate before rotating.
The order of transformations is important (e.g. translating, then rotating will be a different location than rotation, then translating).
In your case image(img, x, y) makes it easy to miss that behind the scenes it's more like translate(x,y);image(img, 0, 0);.
I recommend:
void draw() {
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
float r = int(random(4)) * HALF_PI; // I added this
translate(100 * i, 100 * j); // translate first
rotate(r); // I added this
int rand_index= int(random(img_count));
image(img[rand_index], 0, 0, 100, 100 );
}
}
}
(depending on your setup, you might find imageMode(CENTER); (in setup()) handy to rotate from image centre (as opposed to top left corner (default)))
It's easy to blend() two images using p5.js, which is great, but I would like to be able to equalize the histogram of the resulting blended image. Something like -equalize in ImageMagick. Does anyone know how to do this in p5.js?
You can do this, sure. Algo: For each channel calculate cumulative normalized histograms multiplied by maximum value in that channel - this will be your new pixel values in channel given old value.
I've read algo description in GeeksforGeeks portal and ported it to p5.js, code:
let img
let mod
// helpers
function getMappings() {
hists = [[], [], []];
newlevels = [[], [], []];
maxval = [-1, -1, -1];
// RGB histograms & maximum pixel values
for (let i = 0; i < img.width; i++) {
for (let j = 0; j < img.height; j++) {
let c = img.get(i, j);
hists[0][c[0]] = (hists[0][c[0]] || 0) + 1;
hists[1][c[1]] = (hists[1][c[1]] || 0) + 1;
hists[2][c[2]] = (hists[2][c[2]] || 0) + 1;
for (let ch=0; ch < 3; ch++) {
if (c[ch] > maxval[ch]) {
maxval[ch] = c[ch];
}
}
}
}
// New intensity levels based on cumulative, normalized histograms
for (let hi = 0; hi < 3; hi++) {
let acc = 0;
for (let lev=0; lev < 256; lev++) {
acc += hists[hi][lev];
newlevels[hi][lev] = Math.round(maxval[hi]*acc/(img.width*img.height));
}
}
return newlevels;
}
function equalizeHistograms() {
let map = getMappings();
for (let i = 0; i < mod.width; i++) {
for (let j = 0; j < mod.height; j++) {
let c = img.get(i, j);
let newcol = color(map[0][c[0]], map[1][c[1]], map[2][c[2]]);
mod.set(i, j, newcol);
}
}
mod.updatePixels();
}
// system functions
function preload() {
img = loadImage('https://i.imgur.com/00HxCYr.jpg');
mod = createImage(200, 140);
}
function setup() {
img.loadPixels();
mod.loadPixels();
createCanvas(250, 400);
equalizeHistograms();
}
function draw() {
image(img,0,0);
image(mod,0,140);
}
DEMO
I have two images. One has more green color and another one has better quality (it has right color). How can I improve the first one to have the similar color as the second one.I used the contrast enhancement as
//Contrast enhancement
for (int y = 0; y < rotated.rows; y++)
{
for (int x = 0; x < rotated.cols; x++)
{
for (int c = 0; c < 3; c++)
{
//"* Enter the alpha value [1.0-3.0]: "
//"* Enter the beta value [0-100]: ";
rotated.at<Vec3b>(y, x)[c] =
saturate_cast<uchar>(2.5*(rotated.at<Vec3b>(y, x)[c]) + 30);
}
}
}
It brightens the image. But I like to have similar color as the second one. What are the RGB values to change to have the second image's color.
For contrast enhancement you can use the equivalent of Matlab imadjust. You can find an OpenCV implementation here.
Applying imadjust with default parameters on each separate channel you get:
Here the full code:
#include <opencv2\opencv.hpp>
#include <vector>
#include <algorithm>
using namespace std;
using namespace cv;
void imadjust(const Mat1b& src, Mat1b& dst, int tol = 1, Vec2i in = Vec2i(0, 255), Vec2i out = Vec2i(0, 255))
{
// src : input CV_8UC1 image
// dst : output CV_8UC1 imge
// tol : tolerance, from 0 to 100.
// in : src image bounds
// out : dst image buonds
dst = src.clone();
tol = max(0, min(100, tol));
if (tol > 0)
{
// Compute in and out limits
// Histogram
vector<int> hist(256, 0);
for (int r = 0; r < src.rows; ++r) {
for (int c = 0; c < src.cols; ++c) {
hist[src(r, c)]++;
}
}
// Cumulative histogram
vector<int> cum = hist;
for (int i = 1; i < hist.size(); ++i) {
cum[i] = cum[i - 1] + hist[i];
}
// Compute bounds
int total = src.rows * src.cols;
int low_bound = total * tol / 100;
int upp_bound = total * (100 - tol) / 100;
in[0] = distance(cum.begin(), lower_bound(cum.begin(), cum.end(), low_bound));
in[1] = distance(cum.begin(), lower_bound(cum.begin(), cum.end(), upp_bound));
}
// Stretching
float scale = float(out[1] - out[0]) / float(in[1] - in[0]);
for (int r = 0; r < dst.rows; ++r)
{
for (int c = 0; c < dst.cols; ++c)
{
int vs = max(src(r, c) - in[0], 0);
int vd = min(int(vs * scale + 0.5f) + out[0], out[1]);
dst(r, c) = saturate_cast<uchar>(vd);
}
}
}
int main()
{
Mat3b img = imread("path_to_image");
vector<Mat1b> planes;
split(img, planes);
for (int i = 0; i < 3; ++i)
{
imadjust(planes[i], planes[i]);
}
Mat3b result;
merge(planes, result);
return 0;
}
How can I find the number of blobs in a 2d matrix? SIZE MxN
A blob is a block of continuous X pixels. where the matrix contains X and O
XOOOXO
OXOXOX
XXOOXO
I would like to use 8-neighbourship (see here). So I would expect 2 blobs to be found in above example.
The idea is simple: Mark each continuous blob and count how many blobs were marked.
Here is some pseudo-code (you did not specify a programming language) to get you started:
numBlobs = 0;
foreach(item in matrix)
{
res = Visit(item);
if(res > 0)
{
numBlobs = numBlobs + 1;
}
}
return numBlobs;
The Visit function/method looks like this:
Visit(item)
{
marked = 0;
if(item.IsX() && !IsItemMarked(neighbour))
{
MarkItemAsVisited(item);
marked = 1;
foreach(neighbour in GetNeighbours(item))
{
marked = marked + Visit(neighbour);
}
}
return marked;
}
All you have to do is to implement the other fucntions/methods but they are pretty straightforward.
public static void main(String[] args) {
int[][] matrix = new int[6][5];
System.out.println(matrix.length);
for (int i=0; i < matrix.length; i++) {
for (int j=0; j < matrix[i].length; j++) {
matrix[i][j] = 0;
}
}
matrix[0][3] = 1;
matrix[1][1] = 1;
matrix[1][3] = 1;
matrix[2][1] = 1;
matrix[2][2] = 1;
matrix[2][3] = 1;
matrix[4][0] = 1;
matrix[4][4] = 1;
matrix[5][2] = 1;
matrix[5][3] = 1;
matrix[5][4] = 1;
System.out.println(findBlobCount(matrix, matrix.length, matrix[0].length));
}
static int findBlobCount (int matrix[][], int rowCount, int colCount)
{
int visited[][] = new int[rowCount][colCount]; // all initialized to false
int count=0;
for (int i=0; i<rowCount; i++)
{
for (int j=0; j<colCount; j++)
{
if (matrix[i][j] == 1 && visited[i][j] == 0) // unvisited black cell
{
markVisited (i,j, matrix, visited, rowCount, colCount);
count++;
}
}
}
return count;
}
static int markVisited (int i, int j, int [][]matrix, int [][]visited, int rowCount, int colCount)
{
if (i < 0 || j < 0)
return 0;
if (i >= rowCount || j >= colCount)
return 0;
if (visited[i][j] == 1) // already visited
return 1;
if (matrix[i][j] == 0) // not a black cell
return 0;
visited[i][j] = 1;
// recursively mark all the 4 adjacent cells - right, left, up and down
return markVisited (i+1, j, matrix, visited, rowCount, colCount)
+ markVisited (i-1, j, matrix, visited, rowCount, colCount)
+ markVisited (i, j+1, matrix, visited, rowCount, colCount)
+ markVisited (i, j-1, matrix, visited, rowCount, colCount);
}
I'm currently working on my own little online pixel editor.
Now I'm trying to add a rotation function.
But I can't quite figure out how to realize it.
Here is the basic query for my pixel grid:
for (var y = 0;y < pixelAmount;y++) {
for (var x = 0;x < pixelAmount;x++) {
var name = y + "x" + x;
newY = ?? ;
newX = ?? ;
if ($(newY + "x" + newX).style.backgroundColor != "rgb(255, 255, 255)")
{ $(name).style.backgroundColor = $(newY + "x" + newX).style.backgroundColor; }
}
}
How do I calculate newY and newX?
How do you rotate a two dimensional array?
from this^ post I got this method (in c#):
int a[4][4];
int n=4;
int tmp;
for (int i=0; i<n/2; i++){
for (int j=i; j<n-i-1; j++){
tmp=a[i][j];
a[i][j]=a[j][n-i-1];
a[j][n-i-1]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[n-j-1][i];
a[n-j-1][i]=tmp;
}
}
or this one:
int[,] array = new int[4,4] {
{ 1,2,3,4 },
{ 5,6,7,8 },
{ 9,0,1,2 },
{ 3,4,5,6 }
};
int[,] rotated = RotateMatrix(array, 4);
static int[,] RotateMatrix(int[,] matrix, int n) {
int[,] ret = new int[n, n];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
ret[i, j] = matrix[n - j - 1, i];
}
}
return ret;
}
the first method doesn't use a second array (/matrix) to save memory..
Take a look at this doc (Section 3: Rotating a bitmap with an angle of any value). It walks you through how to do the math and gives you some sample code (C++, but it should be good enough for what you need).
If very quick performance is not of huge importance (which is the case by default), you can consider rotating the picture clockwise by flipping it against the main diagonal and then horizontally. To rotate counterclockwise, flip horizontally, then against the main diagonal. The code is much simpler.
For diagonal flip you exchange the values of image[x,y] with image[y,x] in a loop like this
for( var x = 0; x < pixelAmount; ++x )
for( var y = x + 1; y < pixelAmount; ++y )
swap(image[x,y],image[y,x]);
For horizontal flip you do something like
for( var y = 0; y < pixelAmount; ++y )
{
i = 0; j = pixelAmount - 1;
while( i < j ) {
swap( image[i,y], image[j,y] );
++i; --j;
}
}