Converting matrix of colors to image in flutter - flutter

I need to convert List<List<int>>> to an dart:ui.Image. I have an algorithm to convert an integer to a color. I tried to do this by drawing one-pixel rectangles on the dart:ui.Canvas, but it is about 100 times slower than I expected! _getPixelColor is the method by which I convert int to Color. Here is my code:
Future<void> matrixToImage(FField sourceMatrix) async {
PictureRecorder p = PictureRecorder();
Canvas c = Canvas(
p,
Rect.fromLTWH(0, 0, sourceMatrix.length.toDouble(),
sourceMatrix[0].length.toDouble()));
Paint paint = Paint();
for (int x = 0; x < sourceMatrix.length; x++) {
for (int y = 0; y < sourceMatrix[0].length; y++) {
int pixelValue = sourceMatrix[x][y];
paint.color = _getPixelColor(pixelValue / 40 / paletteLength + paletteOffset);
c.drawRect(Rect.fromLTWH(x.toDouble(), y.toDouble(), 1, 1), paint);
}
}
Picture picture = p.endRecording();
Image result = await picture.toImage(sourceMatrix.length, sourceMatrix[0].length);
}

If you can compute your int value to an int value that the Image class understands, you don't need the detour through canvas drawing.
Just use Image.fromBytes with the .value of the Color as the list of ints.

That depends on how the image data is stored in the list of INTs.
Generally, I like this package to manipulate images and display them:
https://pub.dev/packages/image
import 'package:image/image.dart' as im;
im.Image? img = decodeImage(bytes); // different-IMAGE class !
Uint8List uint8list = getEncodedJpg(img!,quality: 85) as Uint8List;
// ...
build(BuildContext context) {
return Container(child:
Image.memory(uint8list) // material-IMAGE class !
);
}

Related

Adding Hover Effect to p5.js Data Visualization not working?

I'm currently working on a data visualization in p5.js. The data is stored in a .csv and the values are mapped to constants. These constants are then used to manipulate the position, color and size of the circles.
Here is the code so far:
let table;
function preload(){
table = loadTable("assets/songs_normalize.csv", "csv", "header");
}
function setup() {
createCanvas (3000,3000);
background(133,124,222);
for (let r = 0; r < table.getRowCount( ); r++) {
const artist = table.getString(r, "artist");
const song = table.getString(r, "song");
const duration = table.getNum(r, "duration_ms");
const year = table.getNum(r,"year");
const tempo = table.getNum (r,"tempo");
const energy = table.getNum (r,"energy");
const popularity = table.getNum(r,"popularity");
const y = map(year,1998,2020,50,2950);
const x = map(tempo,60,211,50,2950);
const size = map(duration,0, 484146, 0, 100);
const color = map (popularity,0,89,0,255)
const opacity = map(energy,0.05,1,0,255)
let circleColor = colorfill(color/2,-color,color);
circleColor.setAlpha(opacity);
noStroke();
fill (circleColor);
circle(x,y,size);
}
}
function colorfill(r,g,b) {
return color(r, g, b);
}
And this is what it looks like:
Now I want to add a simple hover effect.
I found a tutorial online that seems to do just what I need but when applied to my project, no circles are drawn:
let table;
function preload(){
table = loadTable("assets/songs_normalize.csv", "csv", "header");
}
function setup() {
createCanvas (3000,3000);
background(133,124,222);
for (let r = 0; r < table.getRowCount( ); r++) {
const artist = table.getString(r, "artist");
const song = table.getString(r, "song");
const duration = table.getNum(r, "duration_ms");
const year = table.getNum(r,"year");
const tempo = table.getNum (r,"tempo");
const energy = table.getNum (r,"energy");
const popularity = table.getNum(r,"popularity");
const y = map(year,1998,2020,50,2950);
const x = map(tempo,60,211,50,2950);
const size = map(duration,0, 484146, 0, 100);
const color = map (popularity,0,89,0,255)
const opacity = map(energy,0.05,1,0,255)
let circleColor = colorfill(color/2,-color,color);
circleColor.setAlpha(opacity);
noStroke();
//fill (circleColor);
//circle(x,y,size);
}
}
function draw() {}
//hover
let dis = dist(mouseX, mouseY, x, y);
if (dis < 5) {
fill(0);
noStroke();
circle(x, y, size);
//draw information
text(artist,x,y);
text(song,x,y+10);
} else {
fill(circleColor);
noStroke();
circle(x, y, size);
}
function colorfill(r,g,b) {
return color(r, g, b);
}
You forgot to include the csv file while posting the question , However i managed to find that on kaggle csv .
Here is your solution - click here
There are several problems with your code
for Hover effects to work on canvas , we need to check the distance between the points on every frame , so the hover code must be inside draw function (your draw function is empty)
to check the distance between your mouse and each circle their co-ordinates and size must be stored somewhere , In the solution i stored them in an array of objects named songs

Flutter canvas not rendering as expected

I am trying to render a grid with flutter canvas with Canvas.drawLine method.
Some lines are rendered semi-transparent and some are not even being rendered on the canvas.
class Background extends PositionComponent with HasGameRef<RokokoGame>{
Offset start = Offset.zero;
Offset end = Offset.zero;
// will be different across devices
late final double canvasX;
late final double canvasY;
final int cellSize = GameConfig.cellSize;
Background(this.canvasX, this.canvasY);
#override
Future<void>? onLoad() {
start = Offset(0, 0);
end = Offset(this.canvasX, this.canvasY);
}
#override
void render(Canvas canvas) {
canvas.drawRect(Rect.fromPoints(Offset.zero, end), Styles.white);
_drawVerticalLines(canvas);
_drawHorizontalLines(canvas);
}
void _drawVerticalLines(Canvas c) {
for (double x = start.dx; x <= end.dx; x += cellSize) {
c.drawLine(Offset(x, start.dy), Offset(x, end.dy), Styles.red);
}
}
void _drawHorizontalLines(Canvas c) {
for (double y = start.dy; y <= end.dy; y += cellSize) {
c.drawLine(Offset(start.dx, y), Offset(end.dx, y), Styles.blue);
}
}
}
So, i was able to solve the issue by applying some stroke width in my Styles class.
class Styles {
static Paint white = BasicPalette.white.paint();
static Paint blue = BasicPalette.blue.paint()..strokeWidth = 3;
static Paint red = BasicPalette.red.paint()..strokeWidth = 3;
}

Dart Fails to save Bytes to PNG, JPEG

I have been trying for hours to figure out why my code is not working. Basically, I have an image. I load its bytes into dart as a list of Uint8List. Then, I replace the values of the list with some other values. The problem is that after replacing the values, when I call the File().writeAsBytes() function, the image is CORRUPTED. Don't know why this is happening. Tried doing everything I could.
var b = File("assets/1K91k (1).jpg").readAsBytesSync();
void main() {
runApp(const MyApp());
for (int i = 0; i < b.length; i++) {
double check = b[i] / 255;
if (check > 0.8) {
b[i] = 255;
} else {
b[i] = 2;
}
}
File("/home/kq1231/Desktop/test.jpg")
..createSync()
..writeAsBytesSync(b);
}
I tried converting the b list to a Uint8List but to no avail.
Feels funny to answer my own question but here's how it got working:
import 'package:image/image.dart' as image;
import 'dart:io';
image.Image prettify(String fileName, String exportPath, String imageName,
double threshold, int blurRadius) {
var b = image.decodeImage(File(fileName).readAsBytesSync());
b = image.gaussianBlur(b!, blurRadius);
for (int i = 0; i < b.width; i++) {
for (int j = 0; j < b.height; j++) {
var pix = b.getPixel(i, j);
if ((image.getBlue(pix) + image.getRed(pix) + image.getGreen(pix)) /
(255 * 3) >
threshold) {
b.setPixel(i, j, 0xffffff);
} else {
b.setPixel(i, j, 0);
}
}
}
File("$exportPath/$imageName")
..createSync()
..writeAsBytesSync(image.encodePng(b));
return b;
}
This is a function called prettify. It applies a specific operation to a given image. First, decode the image. Then, loop through each pixel, average the R, G and B values to get the grayscale value (get the value of the pixel using image.getPixel() and set its value using image.setPixel()). Then, encode it back to .png format and save it.
Note that image is the name of the library imported.

Mask out color in Image canvas flutter

I am trying to add skin tone to an image in flutter canvas.
I've used the following code before to apply chroma by altering pixels when I load the image:
static Future<ui.Image> applyChromaToImage(ui.Image image, String pathForImage, RGBPixel chromaToApply, {RGBPixel previousChromaColor}) async
{
List<ChromaPointRange> chromaPoints = processChromaImageBytes(
image, imgBytes);
for(ChromaPointRange rnge in chromaPoints)
{
for(int y = rnge.yValStart; y <= rnge.yValEnd; y++)
{
RGBPixel currentPixel = RGBPixel.generatePixelFromImagePos(imgBytes, image.width, rnge.xVal, y);
//replace current pixel with skin tone
RGBPixel newPixl = currentPixel.mergeSkinTonePixel(chromaToApply, previousChromaColor);
imgBytes.setUint32((y * image.width + rnge.xVal) * 4, newPixl.getHex());
}
}
final Completer<ui.Image> imageCompleter = new Completer();
//looks the endian format doesn't get set right here
ui.PixelFormat formatToUse = Endian.host == Endian.little ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888;
ui.decodeImageFromPixels(
imgBytes.buffer.asUint8List(),
image.width,
image.height,
formatToUse,
(ui.Image result) {
imageCompleter.complete(result);
// use your result image
},
);
//return image;
return await imageCompleter.future;
}
static List<ChromaPointRange> processChromaImageBytes(ui.Image image, ByteData imgBytes)
{
List<ChromaPointRange> chromaPoints = [];
ChromaPointRange currentPoints = null;
for(int x = 0; x < image.width; x = x + 1)
{
for(int y = 0; y < image.height; y = y + 1)
{
RGBPixel currentPixel = RGBPixel.generatePixelFromImagePos(imgBytes, image.width, x, y);
if(currentPixel.isSkinTonePixel())
{
if(currentPoints == null)
{
currentPoints = ChromaPointRange.fromEmpty();
currentPoints.xVal = x;
currentPoints.yValStart = y;
}
}
else if(currentPoints != null)
{
currentPoints.yValEnd = y - 1;
chromaPoints.add(currentPoints);
currentPoints = null;
}
}
if(currentPoints != null)
{
currentPoints.yValEnd = image.height - 1;
chromaPoints.add(currentPoints);
currentPoints = null;
}
}
return chromaPoints;
}
which basically checks every pixel in the image to see if it's within a range of the target color ( with is RGB 0, 255, 0), then adjusts the pixel if it is. This works, but takes a really long time ~ 3 seconds for a 1920 x 1080 image.
The end result is that I want to paint the image to a canvas with a skin tone applied. I've tried a different strategy, by painting the color underneath the image, and then trying to mask out that color from the image using canvas color filters. This is 1000% faster, but doesn't quite work.
Here is the code:
renderSprite(Canvas canvasToRender, Offset offsetToRender)
{
Paint imgPaint = new Paint();
if(chromaToApply != null)
{
Paint chromaPaint = new Paint();
chromaPaint.colorFilter = ColorFilter.mode(Color.fromRGBO(chromaToApply.redVal, chromaToApply.greenVal, chromaToApply.blueVal, 1), BlendMode.modulate);
canvasToRender.drawImage(spriteImage, offsetToRender, chromaPaint);
imgPaint.colorFilter = ColorFilter.mode(Color.fromRGBO(0, 255, 0, 1), BlendMode.dstOut);
}
if(spriteImage != null)
canvasToRender.drawImage(spriteImage, offsetToRender, imgPaint);
}
Here is the image that is painted underneath
Here is the image that is painted ontop
So I'm trying to mask out the green so the tan skin tone shows through on specific parts of the image.
I can't seem to find any combination of ColorFilter or anything else that will mask out the green color for me from the canvas. Any suggestions?

Image to RGB matrix in flutter

I am a beginner in Flutter. I want to choose an image from my local and convert it into an RGB array. Can anybody please provide me with the code to do so correctly?
You can use this package - image
It provides image conversion and manipulation utility functions.
import 'package:image/image.dart' as Imagi;
Here's how to use it to obtain RGB matrix of a file from ImagePicker()-
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
final imageTemp = File(image.path);
controlImage = imageTemp;
Now here's a function for obtaining the RGB matrix -
List<List<int>> imgArray = [];
void readImage() async{
final bytes = await controlImage!.readAsBytes();
final decoder = Imagi.JpegDecoder();
final decodedImg = decoder.decodeImage(bytes);
final decodedBytes = decodedImg!.getBytes(format: Imagi.Format.rgb);
// print(decodedBytes);
print(decodedBytes.length);
// int loopLimit = decodedImg.width;
int loopLimit =1000;
for(int x = 0; x < loopLimit; x++) {
int red = decodedBytes[decodedImg.width*3 + x*3];
int green = decodedBytes[decodedImg.width*3 + x*3 + 1];
int blue = decodedBytes[decodedImg.width*3 + x*3 + 2];
imgArray.add([red, green, blue]);
}
print(imgArray);
}
The array imgArray will contain the RGB matrix