in Flutter what's the correct way to change pixel data of an image with ui.Image type without using the Image package ? I was able to update the pixels by using the Image package but I don't want to convert the image multiple times. So I'm trying to explore the possibility to update Uint8List data. Below is the code snippet. However, I got "Exception: Invalid image data" when I tried to update the image with manipulated Uint8List. Wonder what I did wrong ? Appreciate any feedback.
int clamp(int x, int a, int b) {
return (x < a)
? a
: (x > b)
? b
: x;
}
int getColorY(int a, int r, int g, int b ) =>
(clamp(a, 0, 255) << 24) |
(clamp(r, 0, 255) << 16) |
(clamp(g, 0, 255) << 8) |
(clamp(b, 0, 255));
Future<ui.Image> setImageData(ui.Image uiX) async
int w = uiX.width;
int h = uiX.height;
//get byteData
final rgbaImageData =
await uiX.toByteData(format: ui.ImageByteFormat.png);
// convert to Uint32
Uint32List words = Uint32List.view(
rgbaImageData.buffer,
rgbaImageData.offsetInBytes,
rgbaImageData.lengthInBytes ~/ Uint32List.bytesPerElement);
int a = 0;
int r = 0;
int g = 0;
int b = 0;
for (int idx = 0; idx < words.length; idx++) {
Color color = Color(words[idx]);
if (color.red > 128) {
a = 0;
} else {
r = 128;
g = 135;
b = 110;
}
words[idx] = getColorY(a, r, g, b);
}
//convert Uint32List to Uint8List
Uint8List bytes = words.buffer.asUint8List();
final Completer<ui.Image> imageCompleter = new Completer();
ui.decodeImageFromList(bytes, (ui.Image img) {
imageCompleter.complete(img);
});
return imageCompleter.future;
}
You can convert ui.Image to raw byte data, manipulate it, then convert it back to ui.Image using ui.imageFromBytes.
final ui.Image image = ...;
// Convert to raw rgba
final ByteData bytes = image.toByteData(format:
ImageByteFormat.rawRgba,
);
// Set the first pixel of the image to red.
bytes.setUint32(0, 0xFF0000FF);
// Set pixel at (x, y) to green.
final x = 10;
final y = 10;
bytes.setUint32((y * image.width + x) * 4, 0x00FF00FF);
ui.decodeImageFromPixels(
bytes.buffer.asUint8List(),
image.width,
image.height,
ui.PixelFormat.rgba8888,
(ui.Image result) {
// use your result image
},
);
The wrong part in your code is:
final rgbaImageData = await uiX.toByteData(format: ui.ImageByteFormat.png);
As format: specifies the format of the returned bytes. So, you had to pass ui.ImageByteFormat.rawRgba.
Also, Color(int) expects an ARGB format instead of RGBA.
You can safely convert package:image/image.dart to package:flutter/src/widgets/image.dart and vice-versa (convert Image class to UI Image Widget) just remember to add header information if it's missing.
See a more detailed answer here:
https://stackoverflow.com/a/72871105/706387
I am building an application which has a Camera inside.
After I take a photo, I want to analyze it to know the brightness of this picture, if it is bad I have to take again the photo.
This is my code right now, it's a javascript function that I found and writing in Dart:
Thanks to #Abion47
EDIT 1
for (int i = 0; i < pixels.length; i++) {
int pixel = pixels[i];
int b = (pixel & 0x00FF0000) >> 16;
int g = (pixel & 0x0000FF00) >> 8;
int r = (pixel & 0x000000FF);
avg = ((r + g + b) / 3).floor();
colorSum += avg;
}
brightness = (colorSum / (width * height)).floor();
}
brightness = (colorSum / (width * height)).round();
// I tried with this other code
//brightness = (colorSum / pixels.length).round();
return brightness;
But I've got less brightness on white than black, the numbers are a little bit weird.
Do you know a better way to know the brightness?
SOLUTION:
Under further investigation we found the solution, we had an error doing the image decoding, but we used a Image function to do it.
Here is our final code:
Image image = decodeImage(file.readAsBytesSync());
var data = image.getBytes();
var colorSum = 0;
for(var x = 0; x < data.length; x += 4) {
int r = data[x];
int g = data[x + 1];
int b = data[x + 2];
int avg = ((r + g + b) / 3).floor();
colorSum += avg;
}
var brightness = (colorSum / (image.width * image.height)).floor();
return brightness;
Hope it helps you.
There are several things wrong with your code.
First, you are getting a range error because you are attempting to access a pixel that doesn't exist. This is probably due to width and/or height being greater than the image's actual width or height. There are a lot of ways to try and get these values, but for this application it doesn't actually matter since the end result is to get an average value across all pixels in the image, and you don't need the width or height of the image for that.
Second, you are fetching the color values by serializing the color value into a hex string and then parsing the individual channel substrings. Your substring is going to result in incorrect values because:
foo.substring(a, b) takes the substring of foo from a to b, exclusive. That means that a and b are indices, not lengths, and the resulting string will not include the character at b. So assuming hex is "01234567", when you do hex.substring(0, 2), you get "01", and then you do hex.substring(3, 5) you get "34" while hex.substring(6, 8) gets you "67". You need to do hex.substring(0, 2) followed by hex.substring(2, 4) and hex.substring(4, 6) to get the first three channels.
That being said, you are fetching the wrong channels. The image package stores its pixel values in ABGR format, meaning the first two characters in the hex string are going to be the alpha channel which is unimportant when calculating image brightness. Instead, you want the second, third, and forth channels for the blue, green, and red values respectively.
And having said all that, this is an extremely inefficient way to do this anyway when the preferred way to retrieve channel data from an integer color value is with bitwise operations on the integer itself. (Never convert a number to a string or vice versa unless you absolutely have to.)
So in summary, what you want will likely be something akin to the following;
final pixels = image.data;
double colorSum = 0;
for (int i = 0; i < pixels.length; i++) {
int pixel = pixels[i];
int b = (pixel & 0x00FF0000) >> 16;
int g = (pixel & 0x0000FF00) >> 8;
int r = (pixel & 0x000000FF);
avg = (r + g + b) / 3;
colorSum += avg;
}
return colorSum / pixels.length;
I would like to get a background subtraction method for outdoor conditions, capable of gradually adjust itself to environment light variations but with the capacity of revealing a presence even that is not in motion.
The problem with adaptive opencv background subtraction methods is that they are only capable to detect a presence when it is moving. On the other hand, old background subtraction methods do not work when the conditions of light are not always the same.
In order to get this I’ve modified the Golan Levin’s method in the video library of Processing (actual frames are compared with a first initial frame), setting a certain low difference threshold.
I therefore assume that all changes over that threshold are due to presence (persons, animals, etc), and changes below this are due to progressive light conditions, and I put this changed pixel in the background’s pixels array.
/* auto-updating background part*/
diferencia = diffR+diffG+diffB;
if (diferencia<minDif) backgroundPixels[i]=video.pixels[i];
That’s not working satisfactorily, image gets dirty, far off from being homogenous. Any idea of how to achieve this would be extremely welcome.
I post the whole code, if it could be of some help. Thanks a lot for your time.
import processing.video.*;
int numPixels;
int[] backgroundPixels;
Capture video;
int camSel=0;
int topDiff=763;
int unbralDif=120;
int mindDif=20;
boolean subtraction, lowSubtr;
PGraphics _tempPG;
void setup() {
size(640, 480);
_tempPG=createGraphics(width, height);
if (camSel==0)video = new Capture(this, width, height);
else video = new Capture(this, width, height, Capture.list()[1]);
video.start();
numPixels = video.width * video.height;
backgroundPixels = new int[numPixels];
loadPixels();
}
void draw() {
if (video.available()) {
video.read();
video.loadPixels();
int presenceSum = 0;
for (int i = 0; i < numPixels; i++) {
color currColor = video.pixels[i];
color bkgdColor = backgroundPixels[i];
int currR = (currColor >> 16) & 0xFF;
int currG = (currColor >> 8) & 0xFF;
int currB = currColor & 0xFF;
int bkgdR = (bkgdColor >> 16) & 0xFF;
int bkgdG = (bkgdColor >> 8) & 0xFF;
int bkgdB = bkgdColor & 0xFF;
int diffR = abs(currR - bkgdR);
int diffG = abs(currG - bkgdG);
int diffB = abs(currB - bkgdB);
presenceSum += diffR + diffG + diffB;
pixels[i] = 0xFF000000 | (diffR << 16) | (diffG << 8) | diffB;
/* auto-updating background part*/
int diferencia = diffR+diffG+diffB;
//detect pixels that have change below a threshold
if (lowSubtr && diferencia<mindDif) {
/* substitute with them the backgound img array */
backgroundPixels[i]=video.pixels[i];
}
/* end auto-updating background part*/
}
updatePixels();
}
subtraction=false;
}
void keyPressed() {
if (keyPressed)startSubtr();
}
void startSubtr() {
arraycopy(video.pixels, backgroundPixels);
lowSubtr=true;
}
void actualizacion(int[] _srcArr, int[] _inputArr, int _ind) {
for (int i=0; i<_srcArr.length; i++) {
_srcArr[_ind]=_inputArr[i];
}
}
I want to compare one bitmap with another bitmap (reference bitmap) and draw all the difference of it in resultant bit map.
Using below code I am able to draw only difference area but not with exact color of it.
Here is my code
Bitmap ResultantBitMap = new Bitmap(bitMap1.Height, bitMap2.Height);
BitmapData bitMap1Data = bitMap1.LockBits(new Rectangle(0, 0, bitMap1.Width, bitMap1.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
BitmapData bitMap2Data = bitMap2.LockBits(new Rectangle(0, 0, bitMap2.Width, bitMap2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
BitmapData bitMapResultantData = ResultantBitMap.LockBits(new Rectangle(0, 0, ResultantBitMap.Width, ResultantBitMap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr scan0 = bitMap1Data.Scan0;
IntPtr scan02 = bitMap2Data.Scan0;
IntPtr scan0ResImg1 = bitMapResultantData.Scan0;
int bitMap1Stride = bitMap1Data.Stride;
int bitMap2Stride = bitMap2Data.Stride;
int ResultantImageStride = bitMapResultantData.Stride;
for (int y = 0; y < bitMap1.Height; y++)
{
//define the pointers inside the first loop for parallelizing
byte* p = (byte*)scan0.ToPointer();
p += y * bitMap1Stride;
byte* p2 = (byte*)scan02.ToPointer();
p2 += y * bitMap2Stride;
byte* pResImg1 = (byte*)scan0ResImg1.ToPointer();
pResImg1 += y * ResultantImageStride;
for (int x = 0; x < bitMap1.Width; x++)
{
//always get the complete pixel when differences are found
if (Math.Abs(p[0] - p2[0]) >= 20 || Math.Abs(p[1] - p2[1]) >= 20 || Math.Abs(p[2] - p2[2]) >= 20)
{
pResImg1[0] = p2[0];// B
pResImg1[1] = p2[1];//R
pResImg1[2] = p2[2];//G
pResImg1[3] = p2[3];//A (Opacity)
}
p += 4;
p2 += 4;
pResImg1 += 4;
}
}
bitMap1.UnlockBits(bitMap1Data);
bitMap2.UnlockBits(bitMap2Data);
ResultantBitMap.UnlockBits(bitMapResultantData);
ResultantBitMap.Save(#"c:\\abcd\abcd.jpeg");
What I want is the difference image with exact color of the reference image.
It's hard to tell what's going on without knowing what all those library calls and "+= 4" are but, are you sure p and p2 correspond to the first and second images of your diagram?
Also, your "Format32bppArgb" format suggests that [0] corresponds to alpha, not to red. Maybe there's a problem with that, too.
Does anybody know how to find the local maxima in a grayscale IPL_DEPTH_8U image using OpenCV? HarrisCorner mentions something like that but I'm actually not interested in corners ...
Thanks!
A pixel is considered a local maximum if it is equal to the maximum value in a 'local' neighborhood. The function below captures this property in two lines of code.
To deal with pixels on 'plateaus' (value equal to their neighborhood) one can use the local minimum property, since plateaus pixels are equal to their local minimum. The rest of the code filters out those pixels.
void non_maxima_suppression(const cv::Mat& image, cv::Mat& mask, bool remove_plateaus) {
// find pixels that are equal to the local neighborhood not maximum (including 'plateaus')
cv::dilate(image, mask, cv::Mat());
cv::compare(image, mask, mask, cv::CMP_GE);
// optionally filter out pixels that are equal to the local minimum ('plateaus')
if (remove_plateaus) {
cv::Mat non_plateau_mask;
cv::erode(image, non_plateau_mask, cv::Mat());
cv::compare(image, non_plateau_mask, non_plateau_mask, cv::CMP_GT);
cv::bitwise_and(mask, non_plateau_mask, mask);
}
}
Here's a simple trick. The idea is to dilate with a kernel that contains a hole in the center. After the dilate operation, each pixel is replaced with the maximum of it's neighbors (using a 5 by 5 neighborhood in this example), excluding the original pixel.
Mat1b kernelLM(Size(5, 5), 1u);
kernelLM.at<uchar>(2, 2) = 0u;
Mat imageLM;
dilate(image, imageLM, kernelLM);
Mat1b localMaxima = (image > imageLM);
Actually after I posted the code above I wrote a better and very very faster one ..
The code above suffers even for a 640x480 picture..
I optimized it and now it is very very fast even for 1600x1200 pic.
Here is the code :
void localMaxima(cv::Mat src,cv::Mat &dst,int squareSize)
{
if (squareSize==0)
{
dst = src.clone();
return;
}
Mat m0;
dst = src.clone();
Point maxLoc(0,0);
//1.Be sure to have at least 3x3 for at least looking at 1 pixel close neighbours
// Also the window must be <odd>x<odd>
SANITYCHECK(squareSize,3,1);
int sqrCenter = (squareSize-1)/2;
//2.Create the localWindow mask to get things done faster
// When we find a local maxima we will multiply the subwindow with this MASK
// So that we will not search for those 0 values again and again
Mat localWindowMask = Mat::zeros(Size(squareSize,squareSize),CV_8U);//boolean
localWindowMask.at<unsigned char>(sqrCenter,sqrCenter)=1;
//3.Find the threshold value to threshold the image
//this function here returns the peak of histogram of picture
//the picture is a thresholded picture it will have a lot of zero values in it
//so that the second boolean variable says :
// (boolean) ? "return peak even if it is at 0" : "return peak discarding 0"
int thrshld = maxUsedValInHistogramData(dst,false);
threshold(dst,m0,thrshld,1,THRESH_BINARY);
//4.Now delete all thresholded values from picture
dst = dst.mul(m0);
//put the src in the middle of the big array
for (int row=sqrCenter;row<dst.size().height-sqrCenter;row++)
for (int col=sqrCenter;col<dst.size().width-sqrCenter;col++)
{
//1.if the value is zero it can not be a local maxima
if (dst.at<unsigned char>(row,col)==0)
continue;
//2.the value at (row,col) is not 0 so it can be a local maxima point
m0 = dst.colRange(col-sqrCenter,col+sqrCenter+1).rowRange(row-sqrCenter,row+sqrCenter+1);
minMaxLoc(m0,NULL,NULL,NULL,&maxLoc);
//if the maximum location of this subWindow is at center
//it means we found the local maxima
//so we should delete the surrounding values which lies in the subWindow area
//hence we will not try to find if a point is at localMaxima when already found a neighbour was
if ((maxLoc.x==sqrCenter)&&(maxLoc.y==sqrCenter))
{
m0 = m0.mul(localWindowMask);
//we can skip the values that we already made 0 by the above function
col+=sqrCenter;
}
}
}
The following listing is a function similar to Matlab's "imregionalmax". It looks for at most nLocMax local maxima above threshold, where the found local maxima are at least minDistBtwLocMax pixels apart. It returns the actual number of local maxima found. Notice that it uses OpenCV's minMaxLoc to find global maxima. It is "opencv-self-contained" except for the (easy to implement) function vdist, which computes the (euclidian) distance between points (r,c) and (row,col).
input is one-channel CV_32F matrix, and locations is nLocMax (rows) by 2 (columns) CV_32S matrix.
int imregionalmax(Mat input, int nLocMax, float threshold, float minDistBtwLocMax, Mat locations)
{
Mat scratch = input.clone();
int nFoundLocMax = 0;
for (int i = 0; i < nLocMax; i++) {
Point location;
double maxVal;
minMaxLoc(scratch, NULL, &maxVal, NULL, &location);
if (maxVal > threshold) {
nFoundLocMax += 1;
int row = location.y;
int col = location.x;
locations.at<int>(i,0) = row;
locations.at<int>(i,1) = col;
int r0 = (row-minDistBtwLocMax > -1 ? row-minDistBtwLocMax : 0);
int r1 = (row+minDistBtwLocMax < scratch.rows ? row+minDistBtwLocMax : scratch.rows-1);
int c0 = (col-minDistBtwLocMax > -1 ? col-minDistBtwLocMax : 0);
int c1 = (col+minDistBtwLocMax < scratch.cols ? col+minDistBtwLocMax : scratch.cols-1);
for (int r = r0; r <= r1; r++) {
for (int c = c0; c <= c1; c++) {
if (vdist(Point2DMake(r, c),Point2DMake(row, col)) <= minDistBtwLocMax) {
scratch.at<float>(r,c) = 0.0;
}
}
}
} else {
break;
}
}
return nFoundLocMax;
}
The first question to answer would be what is "local" in your opinion. The answer may well be a square window (say 3x3 or 5x5) or circular window of a certain radius. You can then scan over the entire image with the window centered at each pixel and pick the highest value in the window.
See this for how to access pixel values in OpenCV.
This is very fast method. It stored founded maxima in a vector of
Points.
vector <Point> GetLocalMaxima(const cv::Mat Src,int MatchingSize, int Threshold, int GaussKernel )
{
vector <Point> vMaxLoc(0);
if ((MatchingSize % 2 == 0) || (GaussKernel % 2 == 0)) // MatchingSize and GaussKernel have to be "odd" and > 0
{
return vMaxLoc;
}
vMaxLoc.reserve(100); // Reserve place for fast access
Mat ProcessImg = Src.clone();
int W = Src.cols;
int H = Src.rows;
int SearchWidth = W - MatchingSize;
int SearchHeight = H - MatchingSize;
int MatchingSquareCenter = MatchingSize/2;
if(GaussKernel > 1) // If You need a smoothing
{
GaussianBlur(ProcessImg,ProcessImg,Size(GaussKernel,GaussKernel),0,0,4);
}
uchar* pProcess = (uchar *) ProcessImg.data; // The pointer to image Data
int Shift = MatchingSquareCenter * ( W + 1);
int k = 0;
for(int y=0; y < SearchHeight; ++y)
{
int m = k + Shift;
for(int x=0;x < SearchWidth ; ++x)
{
if (pProcess[m++] >= Threshold)
{
Point LocMax;
Mat mROI(ProcessImg, Rect(x,y,MatchingSize,MatchingSize));
minMaxLoc(mROI,NULL,NULL,NULL,&LocMax);
if (LocMax.x == MatchingSquareCenter && LocMax.y == MatchingSquareCenter)
{
vMaxLoc.push_back(Point( x+LocMax.x,y + LocMax.y ));
// imshow("W1",mROI);cvWaitKey(0); //For gebug
}
}
}
k += W;
}
return vMaxLoc;
}
Found a simple solution.
In this example, if you are trying to find 2 results of a matchTemplate function with a minimum distance from each other.
cv::Mat result;
matchTemplate(search, target, result, CV_TM_SQDIFF_NORMED);
float score1;
cv::Point displacement1 = MinMax(result, score1);
cv::circle(result, cv::Point(displacement1.x+result.cols/2 , displacement1.y+result.rows/2), 10, cv::Scalar(0), CV_FILLED, 8, 0);
float score2;
cv::Point displacement2 = MinMax(result, score2);
where
cv::Point MinMax(cv::Mat &result, float &score)
{
double minVal, maxVal;
cv::Point minLoc, maxLoc, matchLoc;
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
matchLoc.x = minLoc.x - result.cols/2;
matchLoc.y = minLoc.y - result.rows/2;
return minVal;
}
The process is:
Find global Minimum using minMaxLoc
Draw a filled white circle around global minimum using min distance between minima as radius
Find another minimum
The the scores can be compared to each other to determine, for example, the certainty of the match,
To find more than just the global minimum and maximum try using this function from skimage:
http://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.peak_local_max
You can parameterize the minimum distance between peaks, too. And more. To find minima, use negated values (take care of the array type though, 255-image could do the trick).
You can go over each pixel and test if it is a local maxima. Here is how I would do it.
The input is assumed to be type CV_32FC1
#include <vector>//std::vector
#include <algorithm>//std::sort
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
//structure for maximal values including position
struct SRegionalMaxPoint
{
SRegionalMaxPoint():
values(-FLT_MAX),
row(-1),
col(-1)
{}
float values;
int row;
int col;
//ascending order
bool operator()(const SRegionalMaxPoint& a, const SRegionalMaxPoint& b)
{
return a.values < b.values;
}
};
//checks if pixel is local max
bool isRegionalMax(const float* im_ptr, const int& cols )
{
float center = *im_ptr;
bool is_regional_max = true;
im_ptr -= (cols + 1);
for (int ii = 0; ii < 3; ++ii, im_ptr+= (cols-3))
{
for (int jj = 0; jj < 3; ++jj, im_ptr++)
{
if (ii != 1 || jj != 1)
{
is_regional_max &= (center > *im_ptr);
}
}
}
return is_regional_max;
}
void imregionalmax(
const cv::Mat& input,
std::vector<SRegionalMaxPoint>& buffer)
{
//find local max - top maxima
static const int margin = 1;
const int rows = input.rows;
const int cols = input.cols;
for (int i = margin; i < rows - margin; ++i)
{
const float* im_ptr = input.ptr<float>(i, margin);
for (int j = margin; j < cols - margin; ++j, im_ptr++)
{
//Check if pixel is local maximum
if ( isRegionalMax(im_ptr, cols ) )
{
cv::Rect roi = cv::Rect(j - margin, i - margin, 3, 3);
cv::Mat subMat = input(roi);
float val = *im_ptr;
//replace smallest value in buffer
if ( val > buffer[0].values )
{
buffer[0].values = val;
buffer[0].row = i;
buffer[0].col = j;
std::sort(buffer.begin(), buffer.end(), SRegionalMaxPoint());
}
}
}
}
}
For testing the code you can try this:
cv::Mat temp = cv::Mat::zeros(15, 15, CV_32FC1);
temp.at<float>(7, 7) = 1;
temp.at<float>(3, 5) = 6;
temp.at<float>(8, 10) = 4;
temp.at<float>(11, 13) = 7;
temp.at<float>(10, 3) = 8;
temp.at<float>(7, 13) = 3;
vector<SRegionalMaxPoint> buffer_(5);
imregionalmax(temp, buffer_);
cv::Mat debug;
cv::cvtColor(temp, debug, cv::COLOR_GRAY2BGR);
for (auto it = buffer_.begin(); it != buffer_.end(); ++it)
{
circle(debug, cv::Point(it->col, it->row), 1, cv::Scalar(0, 255, 0));
}
This solution does not take plateaus into account so it is not exactly the same as matlab's imregionalmax()
I think you want to use the
MinMaxLoc(arr, mask=NULL)-> (minVal, maxVal, minLoc, maxLoc)
Finds global minimum and maximum in array or subarray
function on you image