从图片中选择主要颜色

15 浏览
0 Comments

从图片中选择主要颜色

我刚接触Dart/Flutter框架,正在探索它们的可能性。

我知道在Android中,可以通过编程方式拍照并提取主要颜色值。(Android示例

我想知道,如何在纯Dart中实现这一点?我希望它能兼容iOS和Android操作系统。

0
0 Comments

近年来,随着移动设备的普及和人们对图片处理需求的增加,从图片中提取主要颜色成为了一个常见的需求。本文将讨论一个名为"Pick main color from picture"的问题,即如何从图片中提取主要颜色。

问题的解决方法是通过使用Flutter框架和相关库来实现。下面是解决该问题的代码:

import 'dart:async';

import 'dart:typed_data';

import 'dart:ui' as ui;

import 'dart:math';

import 'package:flutter/material.dart';

import 'package:flutter/rendering.dart';

import 'package:image/image.dart' as img;

import 'package:flutter/services.dart' show rootBundle;

void main() => runApp(const MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {

const MyApp({Key? key}) : super(key: key);

State createState() => _MyAppState();

}

class _MyAppState extends State {

String imagePath = 'assets/5.jpg';

GlobalKey imageKey = GlobalKey();

GlobalKey paintKey = GlobalKey();

bool useSnapshot = true;

late GlobalKey currentKey;

final StreamController _stateController = StreamController();

img.Image? photo;

void initState() {

currentKey = useSnapshot ? paintKey : imageKey;

super.initState();

}

Widget build(BuildContext context) {

final String title = useSnapshot ? "snapshot" : "basic";

return SafeArea(

child: Scaffold(

appBar: AppBar(title: Text("Color picker $title")),

body: StreamBuilder(

initialData: Colors.green[500],

stream: _stateController.stream,

builder: (buildContext, snapshot) {

Color selectedColor = snapshot.data as Color ?? Colors.green;

return Stack(

children: [

RepaintBoundary(

key: paintKey,

child: GestureDetector(

onPanDown: (details) {

searchPixel(details.globalPosition);

},

onPanUpdate: (details) {

searchPixel(details.globalPosition);

},

child: Center(

child: Image.asset(

imagePath,

key: imageKey,

fit: BoxFit.contain,

),

),

),

),

Container(

margin: const EdgeInsets.all(70),

width: 50,

height: 50,

decoration: BoxDecoration(

shape: BoxShape.circle,

color: selectedColor!,

border: Border.all(width: 2.0, color: Colors.white),

boxShadow: [

const BoxShadow(

color: Colors.black12,

blurRadius: 4,

offset: Offset(0, 2))

]),

),

Positioned(

child: Text('${selectedColor}',

style: const TextStyle(

color: Colors.white,

backgroundColor: Colors.black54)),

left: 114,

top: 95,

),

],

);

}),

),

);

}

void searchPixel(Offset globalPosition) async {

if (photo == null) {

await (useSnapshot ? loadSnapshotBytes() : loadImageBundleBytes());

}

_calculatePixel(globalPosition);

}

void _calculatePixel(Offset globalPosition) {

RenderBox box = currentKey.currentContext!.findRenderObject() as RenderBox;

Offset localPosition = box.globalToLocal(globalPosition);

double px = localPosition.dx;

double py = localPosition.dy;

if (!useSnapshot) {

double widgetScale = box.size.width / photo!.width;

px = (px / widgetScale);

py = (py / widgetScale);

}

int pixel32 = photo!.getPixelSafe(px.toInt(), py.toInt());

int hex = abgrToArgb(pixel32);

_stateController.add(Color(hex));

}

Future loadImageBundleBytes() async {

ByteData imageBytes = await rootBundle.load(imagePath);

setImageBytes(imageBytes);

}

Future loadSnapshotBytes() async {

RenderRepaintBoundary boxPaint =

paintKey.currentContext!.findRenderObject() as RenderRepaintBoundary;

ui.Image capture = await boxPaint.toImage();

ByteData? imageBytes =

await capture.toByteData(format: ui.ImageByteFormat.png);

setImageBytes(imageBytes!);

capture.dispose();

}

void setImageBytes(ByteData imageBytes) {

List values = imageBytes.buffer.asUint8List();

photo;

photo = img.decodeImage(values)!;

}

}

int abgrToArgb(int argbColor) {

int r = (argbColor >> 16) & 0xFF;

int b = argbColor & 0xFF;

return (argbColor & 0xFF00FF00) | (b << 16) | r;

}

以上代码中,主要的部分是一个名为`_MyAppState`的Widget类,它是`MyApp`类的子类。在`_MyAppState`中,首先定义了一些变量,如`imagePath`表示图片的路径,`imageKey`和`paintKey`表示图片的Key,`useSnapshot`表示是否使用快照等。然后在`initState`方法中初始化了一些变量。`build`方法返回了一个包含图片和颜色选择组件的界面。通过手势识别器,可以在图片上点击或滑动来选择像素点的颜色。`searchPixel`方法用于获取当前像素点的颜色,并通过`_stateController`向界面传递颜色信息。`loadImageBundleBytes`和`loadSnapshotBytes`方法用于加载图片的字节数据。`setImageBytes`方法将字节数据解码为图片,并保存在`photo`变量中。最后,`abgrToArgb`函数用于将图片的像素颜色格式从ABGR转换为ARGB。

通过以上代码,我们可以实现从图片中提取主要颜色的功能,用户可以通过界面上的交互操作来获取颜色信息。

0
0 Comments

问题原因:用户想要从图片中提取主要颜色。

解决方法:使用Flutter团队开发的"Palette Generator"插件。以下是如何使用该插件提取主要颜色的简单示例:

首先,导入所需的库:

import 'package:palette_generator/palette_generator.dart';

然后创建主应用程序类:

class MyApp extends StatelessWidget {

Widget build(BuildContext context) {

return MaterialApp(

...

home: const HomePage(

title: 'Colors from image',

image: AssetImage('assets/images/artwork_default.png',),

imageSize: Size(256.0, 170.0),

...

),

);

}

}

在上面的代码中,将要从中提取主要颜色的图片放在image字段中。

接下来,创建HomePage类:

class HomePage extends StatefulWidget {

const HomePage({

Key key,

this.title,

this.image,

this.imageSize,

}) : super(key: key);

final String title; //应用程序标题

final ImageProvider image; //用于加载颜色的图片提供器

final Size imageSize; //图片尺寸

_HomePageState createState() {

return _HomePageState();

}

}

我们还需要创建_HomePageState:

class _HomePageState extends State<HomePage> {

Rect region;

PaletteGenerator paletteGenerator;

final GlobalKey imageKey = GlobalKey();

void initState() {

super.initState();

region = Offset.zero & widget.imageSize;

_updatePaletteGenerator(region);

}

Future<void> _updatePaletteGenerator(Rect newRegion) async {

paletteGenerator = await PaletteGenerator.fromImageProvider(

widget.image,

size: widget.imageSize,

region: newRegion,

maximumColorCount: 20,

);

setState(() {});

}

Widget build(BuildContext context) {

return Scaffold(

backgroundColor: _kBackgroundColor,

appBar: AppBar(

title: Text(widget.title),

),

body: Column(

mainAxisSize: MainAxisSize.max,

mainAxisAlignment: MainAxisAlignment.start,

crossAxisAlignment: CrossAxisAlignment.center,

children: <Widget>[

new AspectRatio(

aspectRatio: 15 / 15,

child: Image(

key: imageKey,

image: widget.image,

),

),

Expanded(child: Swatches(generator: paletteGenerator)),

],

),

);

}

}

上述代码只是布局了图片和Swatches类(在下面定义)。在initState中,我们首先选择一个从中提取颜色的区域,对于我们的案例来说,就是整个图片。

之后,我们创建了一个接收PaletteGenerator并绘制颜色样本的类Swatches:

class Swatches extends StatelessWidget {

const Swatches({Key key, this.generator}) : super(key: key);

final PaletteGenerator generator;

Widget build(BuildContext context) {

final List<Widget> swatches = <Widget>[];

if (generator == null || generator.colors.isEmpty) {

return Container();

}

for (Color color in generator.colors) {

swatches.add(PaletteSwatch(color: color));

}

return Column(

mainAxisAlignment: MainAxisAlignment.center,

mainAxisSize: MainAxisSize.min,

crossAxisAlignment: CrossAxisAlignment.center,

children: <Widget>[

Wrap(

children: swatches,

),

Container(height: 30.0),

PaletteSwatch(label: 'Dominant', color: generator.dominantColor?.color),

PaletteSwatch(

label: 'Light Vibrant', color: generator.lightVibrantColor?.color),

PaletteSwatch(label: 'Vibrant', color: generator.vibrantColor?.color),

PaletteSwatch(

label: 'Dark Vibrant', color: generator.darkVibrantColor?.color),

PaletteSwatch(

label: 'Light Muted', color: generator.lightMutedColor?.color),

PaletteSwatch(label: 'Muted', color: generator.mutedColor?.color),

PaletteSwatch(

label: 'Dark Muted', color: generator.darkMutedColor?.color),

],

);

}

}

接下来,我们创建了一个PaletteSwatch类,它是一个带有可选标签的颜色方块:

class PaletteSwatch extends StatelessWidget {

const PaletteSwatch({

Key key,

this.color,

this.label,

}) : super(key: key);

final Color color;

final String label;

Widget build(BuildContext context) {

final HSLColor hslColor = HSLColor.fromColor(color ?? Colors.transparent);

final HSLColor backgroundAsHsl = HSLColor.fromColor(_kBackgroundColor);

final double colorDistance = math.sqrt(

math.pow(hslColor.saturation - backgroundAsHsl.saturation, 2.0) +

math.pow(hslColor.lightness - backgroundAsHsl.lightness, 2.0));

Widget swatch = Padding(

padding: const EdgeInsets.all(2.0),

child: color == null

? const Placeholder(

fallbackWidth: 34.0,

fallbackHeight: 20.0,

color: Color(0xff404040),

strokeWidth: 2.0,

)

: Container(

decoration: BoxDecoration(

color: color,

border: Border.all(

width: 1.0,

color: _kPlaceholderColor,

style: colorDistance < 0.2

? BorderStyle.solid

: BorderStyle.none,

)),

width: 34.0,

height: 20.0,

),

);

if (label != null) {

swatch = ConstrainedBox(

constraints: const BoxConstraints(maxWidth: 130.0, minWidth: 130.0),

child: Row(

mainAxisAlignment: MainAxisAlignment.start,

children: <Widget>[

swatch,

Container(width: 5.0),

Text(label),

],

),

);

}

return swatch;

}

}

希望这能帮到你,谢谢。

0
0 Comments

问题的出现原因:在使用Palette Generator库时,需要获取给定图像提供者的主要颜色,但官方文档中的示例代码较为复杂,给开发者带来了困扰。

解决方法:下面提供了一个简单的函数,使用Palette Generator库来获取给定图像提供者的主要颜色,并展示了如何使用FutureBuilder来构建一个Widget。

import 'package:palette_generator/palette_generator.dart';

// Calculate dominant color from ImageProvider

Future getImagePalette (ImageProvider imageProvider) async {

final PaletteGenerator paletteGenerator = await PaletteGenerator

.fromImageProvider(imageProvider);

return paletteGenerator.dominantColor.color;

}

然后可以在输出上使用FutureBuilder来构建一个Widget。

感谢,这段代码简化了官方文档中复杂的部分。

0