四十八、openlayers地图调色总结——锐化、模糊、浮雕滤镜,调整地图色相、饱和度、亮度

news2024/7/6 20:46:08

 

 

这篇是对滤镜的总结,方便工作中直接使用。

想要调整图层的颜色,有两种方法。

方法一:

 加载图层时使用tileLoadFunction函数拿到context添加canvas滤镜效果。

 this.imagery = new TileLayer({
        source: new XYZ({
          url: "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
          crossOrigin: "anonymous",
            tileLoadFunction: function (imageTile, src) {
              let img = new Image();
              // 设置图片不从缓存取,从缓存取可能会出现跨域,导致加载失败
              img.setAttribute("crossOrigin", "anonymous");
              img.onload = function () {
                let canvas = document.createElement("canvas");
                let w = img.width;
                let h = img.height;
                canvas.width = w;
                canvas.height = h;
                let context = canvas.getContext("2d");

                context.filter =
                  "grayscale(0%) invert(15%) sepia(0%) hue-rotate(75deg) saturate(200%) brightness(100%) contrast(100%)";
                // grayscale 灰度   invert反相   sepia将图像转化成深褐色  saturate饱和度   brightness暗度 contrast对比度

                context.drawImage(img, 0, 0, w, h, 0, 0, w, h);
                imageTile.getImage().src = canvas.toDataURL("image/png");
              };
              img.src = src;
            },
        }),
      });
方法二:

利用postrender事件手动获取像素数据修改每一个像素点的值。

   this.imagery.on("postrender", (event) => {
      this.convolve(event.context, this.selectedKernel);
    });
convolve(context, kernel) {
      const canvas = context.canvas;
      const width = canvas.width;
      const height = canvas.height;
      // 假设 kernel 是一个归一化的卷积核矩阵,其大小为 size x size
      const size = Math.sqrt(kernel.length);
      const half = Math.floor(size / 2);
      // 获取输入图像数据
      const inputData = context.getImageData(0, 0, width, height).data;
      // 创建一个新的 ImageData 对象用于输出图像数据
      const output = context.createImageData(width, height);
      const outputData = output.data;
      //   遍历每个像素
      for (let pixelY = 0; pixelY < height; ++pixelY) {
        const pixelsAbove = pixelY * width;
        for (let pixelX = 0; pixelX < width; ++pixelX) {
          let r = 0,
            g = 0,
            b = 0,
            a = 0;
          // 遍历卷积核
          for (let kernelY = 0; kernelY < size; ++kernelY) {
            for (let kernelX = 0; kernelX < size; ++kernelX) {
              let weight = kernel[kernelY * size + kernelX];
              const neighborY = Math.min(
                height - 1,
                Math.max(0, pixelY + kernelY - half)
              );
              const neighborX = Math.min(
                width - 1,
                Math.max(0, pixelX + kernelX - half)
              );
              const inputIndex = (neighborY * width + neighborX) * 4;

              // 累加加权后的像素值
              r += inputData[inputIndex] * weight;
              g += inputData[inputIndex + 1] * weight;
              b += inputData[inputIndex + 2] * weight;
              a += inputData[inputIndex + 3] * weight;
            }
          }
          const outputIndex = (pixelsAbove + pixelX) * 4;
          outputData[outputIndex] = r;
          outputData[outputIndex + 1] = g;
          outputData[outputIndex + 2] = b;
          outputData[outputIndex + 3] = kernel.normalized ? a : 255; // 如果卷积核是归一化的,则使用计算后的 alpha,否则设为 255
          //添加红绿蓝通道的值
          outputData[outputIndex] *= this.palette.red;
          outputData[outputIndex + 1] *= this.palette.green;
          outputData[outputIndex + 2] *= this.palette.blue;
          //添加亮度
          outputData[outputIndex] += this.palette.brightness;
          outputData[outputIndex + 1] += this.palette.brightness;
          outputData[outputIndex + 2] += this.palette.brightness;
        }
      }
      context.putImageData(output, 0, 0);
    },

 如果要添加模糊、锐化、浮雕等效果需要进行卷积核相关的操作,如果只是修改颜色则不需要。

两种方法总结:

第一种比较简单,代码较少。

第二种代码较多,但是修改的自由度比较高,可以添加更多的效果。 

第二种方法的原理可以看这篇:

四十七、openlayers官网示例Image Filters——给地图添加锐化、浮雕、边缘等滤镜效果-CSDN博客

 完整代码:

<template>
  <div class="box">
    <h1>滤镜效果</h1>
    <div id="map" class="map"></div>
    <div class="tools">
      <select
        id="kernel"
        name="kernel"
        style="height: 20px; margin-right: 20px"
        @change="paletteChange"
        v-model="kernelValue"
      >
        <option v-for="(value, key, index) in kernels" :key="index">
          {{ key }}
        </option>
      </select>
      <div>
        <label for="brightness">亮度:</label>
        <input
          type="range"
          v-model.number="palette.brightness"
          id="brightness"
          min="-100"
          max="100"
          value="0"
          @change="paletteChange"
        />
        <br />
        <label for="redInput">红:</label>
        <input
          type="range"
          v-model="palette.red"
          id="redInput"
          min="0"
          max="2"
          step="0.01"
          @change="paletteChange"
        />
        <br />
        <label for="redInput">绿:</label>
        <input
          type="range"
          v-model="palette.green"
          id="greenInput"
          min="0"
          max="2"
          step="0.01"
          @change="paletteChange"
        />
        <br />
        <label for="redInput">蓝:</label>
        <input
          type="range"
          v-model="palette.blue"
          id="blueInput"
          min="0"
          max="2"
          step="0.01"
          @change="paletteChange"
        />
      </div>
    </div>
  </div>
</template>

<script>
import Map from "ol/Map.js";
import View from "ol/View.js";
import XYZ from "ol/source/XYZ.js";
import { fromLonLat } from "ol/proj.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { OGCMapTile, Vector as VectorSource } from "ol/source.js";
export default {
  name: "",
  components: {},
  data() {
    return {
      map: null,
      extentData: "",
      palette: {
        brightness: 0,
        red: 1,
        green: 1,
        blue: 1,
      },
      imagery: null,
      //卷积核
      kernels: {
        none: [0, 0, 0, 0, 1, 0, 0, 0, 0], //无
        sharpen: [0, -1, 0, -1, 5, -1, 0, -1, 0], //锐化滤波器
        sharpenless: [0, -1, 0, -1, 10, -1, 0, -1, 0], //增强图像的边缘和细节,但比 sharpen 更强烈。
        blur: [1, 1, 1, 1, 1, 1, 1, 1, 1], //平滑滤波器,通过对邻域像素值求平均来模糊图像
        shadow: [1, 2, 1, 0, 1, 0, -1, -2, -1], //阴影滤波器
        emboss: [-2, 1, 0, -1, 1, 1, 0, 1, 2], //浮雕滤波器
        edge: [0, 1, 0, 1, -4, 1, 0, 1, 0], //边缘检测滤波器
      },
      kernelValue: "none",
      selectedKernel: "",
    };
  },
  computed: {},
  created() {},
  mounted() {
    this.initMap();

    this.selectedKernel = this.normalize(this.kernels[this.kernelValue]);

    this.imagery.on("postrender", (event) => {
      this.convolve(event.context, this.selectedKernel);
    });
  },
  methods: {
    convolve(context, kernel) {
      const canvas = context.canvas;
      const width = canvas.width;
      const height = canvas.height;
      // 假设 kernel 是一个归一化的卷积核矩阵,其大小为 size x size
      const size = Math.sqrt(kernel.length);
      const half = Math.floor(size / 2);
      // 获取输入图像数据
      const inputData = context.getImageData(0, 0, width, height).data;
      // 创建一个新的 ImageData 对象用于输出图像数据
      const output = context.createImageData(width, height);
      const outputData = output.data;
      //   遍历每个像素
      for (let pixelY = 0; pixelY < height; ++pixelY) {
        const pixelsAbove = pixelY * width;
        for (let pixelX = 0; pixelX < width; ++pixelX) {
          let r = 0,
            g = 0,
            b = 0,
            a = 0;
          // 遍历卷积核
          for (let kernelY = 0; kernelY < size; ++kernelY) {
            for (let kernelX = 0; kernelX < size; ++kernelX) {
              let weight = kernel[kernelY * size + kernelX];
              const neighborY = Math.min(
                height - 1,
                Math.max(0, pixelY + kernelY - half)
              );
              const neighborX = Math.min(
                width - 1,
                Math.max(0, pixelX + kernelX - half)
              );
              const inputIndex = (neighborY * width + neighborX) * 4;

              // 累加加权后的像素值
              r += inputData[inputIndex] * weight;
              g += inputData[inputIndex + 1] * weight;
              b += inputData[inputIndex + 2] * weight;
              a += inputData[inputIndex + 3] * weight;
            }
          }
          const outputIndex = (pixelsAbove + pixelX) * 4;
          outputData[outputIndex] = r;
          outputData[outputIndex + 1] = g;
          outputData[outputIndex + 2] = b;
          outputData[outputIndex + 3] = kernel.normalized ? a : 255; // 如果卷积核是归一化的,则使用计算后的 alpha,否则设为 255
          //添加红绿蓝通道的值
          outputData[outputIndex] *= this.palette.red;
          outputData[outputIndex + 1] *= this.palette.green;
          outputData[outputIndex + 2] *= this.palette.blue;
          //添加亮度
          outputData[outputIndex] += this.palette.brightness;
          outputData[outputIndex + 1] += this.palette.brightness;
          outputData[outputIndex + 2] += this.palette.brightness;
        }
      }
      context.putImageData(output, 0, 0);
    },
    normalize(kernel) {
      // 获取卷积核的长度
      const len = kernel.length;
      // 创建一个与卷积核相同长度的新数组
      const normal = new Array(len);
      let i,
        sum = 0;
      // 计算卷积核中所有元素的总和
      for (i = 0; i < len; ++i) {
        sum += kernel[i];
      }
      // 如果总和小于等于0,设置sum为1并标记为未归一化
      if (sum <= 0) {
        normal.normalized = false;
        sum = 1;
      } else {
        // 如果总和大于0,标记为已归一化
        normal.normalized = true;
      }
      // 将卷积核中的每个元素除以总和,得到归一化后的值
      for (i = 0; i < len; ++i) {
        normal[i] = kernel[i] / sum;
      }
      // 返回归一化后的卷积核
      return normal;
    },
    initMap() {
      this.imagery = new TileLayer({
        source: new XYZ({
          url: "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
          crossOrigin: "anonymous",
            // tileLoadFunction: function (imageTile, src) {
            //   let img = new Image();
            //   // 设置图片不从缓存取,从缓存取可能会出现跨域,导致加载失败
            //   img.setAttribute("crossOrigin", "anonymous");
            //   img.onload = function () {
            //     let canvas = document.createElement("canvas");
            //     let w = img.width;
            //     let h = img.height;
            //     canvas.width = w;
            //     canvas.height = h;
            //     let context = canvas.getContext("2d");

            //     context.filter =
            //       "grayscale(0%) invert(15%) sepia(0%) hue-rotate(75deg) saturate(200%) brightness(100%) contrast(100%)";
            //     // grayscale 灰度   invert反相   sepia将图像转化成深褐色  saturate饱和度   brightness暗度 contrast对比度

            //     context.drawImage(img, 0, 0, w, h, 0, 0, w, h);
            //     imageTile.getImage().src = canvas.toDataURL("image/png");
            //   };
            //   img.src = src;
            // },
        }),
      });
      this.map = new Map({
        layers: [this.imagery],
        target: "map",
        view: new View({
          center: fromLonLat([-120, 50]),
          zoom: 6,
        }),
      });
    },
    paletteChange() {
      console.log("this.palette", this.palette);
      this.selectedKernel = this.normalize(this.kernels[this.kernelValue]);
      this.map.render();
    },
  },
};
</script>

<style lang="scss" scoped>
#map {
  width: 100%;
  height: 500px;
}
.box {
  height: 100%;
}
.tools {
  display: flex;
}
</style>

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1840077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt6视频播放器项目框架代码

视频播放的关键代码如下: 使用Qt6的QMediaPlayer,QVideoWidget实现 void FunnyWidget::initVideo() {player = new QMediaPlayer(this);videoWidget = new QVideoWidget(this);playButton = new QPushButton("Play", this);pauseButton = new QPushButton("…

BLE-蓝牙广播

蓝牙广播&#xff1a;

生信网络学院|06月21日《SolidWorks Costing助力制造企业建立成本核算体系》

课程主题&#xff1a;SolidWorks Costing助力制造企业建立成本核算体系 课程时间&#xff1a;2024年06月21日 14:00-14:30 主讲人&#xff1a;张丹清 生信科技 售前顾问 Costing成本分析简介钣金件成本分析加工件成本分析装配体成本分析总结&答疑 安装腾讯会议客户端或…

CSS实现文字上下滚动、间歇滚动和无限滚动

目录 1、连续滚动2、间歇性向上滚动3、任意个数向上滚动 本文主要记录了如何实现文字上下滚动效果&#xff0c;实现主要就是用到了css3的两个属性&#xff1a; framekeys和 animation 1、连续滚动 <div class"scroll-continuous"><div class"content…

【玩儿】TX应用宝模拟器安装第三方APK

TX应用宝模拟器安装第三方APK 前言应用宝下载安装三方APK安装审核应用安装自研应用安装执行 adb 命令创建应用快捷方式 前言 腾讯应用开放平台公布了应用宝电脑版的产品详情&#xff0c;应用宝电脑版支持从PC桌面直接打开安卓应用&#xff0c;无需感知应用市场打开界面&#x…

【教学类-64-04】20240619彩色鱼骨图(一)6.5*1CM 6根棒子720种

背景需求&#xff1a; 幼儿益智早教玩具❗️鱼骨拼图 - 小红书在家也能自制的木棒鱼骨拼图&#xff0c;你也收藏起来试一试吧。 #母婴育儿 #新手爸妈 #玩具 #宝宝玩具怎么选 #早教 #早教玩具 #幼儿早教 #益智早教 #玩具 #宝宝早教 #益智拼图 #宝宝拼图 #玩不腻的益智玩具 #儿童…

第四篇:精通Docker构建:Dockerfile的艺术与策略

精通Docker构建&#xff1a;Dockerfile的艺术与策略 1. 开篇&#xff1a;探索Docker的革命 在探讨我们的主题之前&#xff0c;让我们先回顾一下Docker的概念。Docker是一个开源平台&#xff0c;用于自动化应用程序的部署、扩展和管理&#xff0c;这一切都是在轻量级的容器中进…

keil5显示内存和存储占用百分比进度条工具

简介 [Keil5_disp_size_bar] 以进度条百分比来显示keil编译后生成的固件对芯片的内存ram和存储flash的占用情况, 并生成各个源码文件对ram和flash的占比整合排序后的map信息的表格和饼图。 原理是使用C语言遍历当前目录找到keil工程和编译后生成的map文件 然后读取工程文件和m…

[16] 使用Opencv_CUDA 实现访问图像像素、直方图均衡化、几何变换

使用Opencv_CUDA 实现访问图像像素、直方图均衡化、几何变换 访问图像的各个像素强度直方图的计算与均衡几何变换&#xff1a;缩放、平移与旋转 1. 访问图像的各个像素强度 #include <iostream> #include "opencv2/opencv.hpp"int main() {cv::Mat h_img1 …

调取Windows系统虚拟键盘

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using System.Diagnostics;namespace 调取Windows虚拟键盘 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void richTextBox1_DoubleClick(object sender, EventArgs e){Proces…

对于补码的个人理解

1. 十进制的取模计算 现在我想要使另一个数加上2后用8取模后也等于1&#xff0c;这个数可以是哪些&#xff1f; 这个问题比较简单&#xff0c;只需要-1加上8的倍数即可 例如&#xff1a; 如果我们想要得到距离-1这个负数最近的一个正数7&#xff0c;直接使用-18即可。反过来想…

JavaSE 面向对象程序设计高级 方法引用 2024详解

在编程中&#xff0c;方法引用&#xff08;Method Reference&#xff09;是一种技术&#xff0c;它让你能够直接引用一个现有的函数或方法&#xff0c;而无需通过对象实例来调用。这种方法在函数式编程和高阶函数中非常有用&#xff0c;因为它提供了简洁的方式来传递函数行为&a…

晋升受阻或遭受不公:你想到这条路了吗?

晋升受阻或遭受不公&#xff1a;你想到这条路了吗&#xff1f; 当你在职场上遇到晋升受阻或者感觉受到不公平对待的时候&#xff0c;你一般选择怎么处理&#xff1f;是逆来顺受、忍气吞声、还是听命安排与据理力争&#xff1f; 其实并非无路可走&#xff0c;这时候&#xff0c;…

在二维空间中用椭圆表示不确定性

在二维空间中用椭圆表示不确定性 flyfish import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Ellipse# 生成示例数据 np.random.seed(0) data np.random.multivariate_normal([0, 0], [[4, 2], [2, 3]], size500)# 计算均值和协方差矩阵 …

使用node把任意网站封装为可执行文件

直接上步骤&#xff1a; 1. node.js 环境准备 下载地址 那个版本都行&#xff0c;下一步->下一步 安装即可 2. windows 系统下&#xff0c; 快捷键 winr ->输入 cmd -> 回车 3. 执行第一个命令&#xff0c;安装 nativefier 等一段时间 npm install nativefier -g 4…

GitHub爆赞!最适合新手入门的教程——笨方法学Python 3

“Python 是一门既容易上手又强大的编程语言。”这句话本身并无大碍&#xff0c;但需要注意的是&#xff0c;正因为它既好学又好用&#xff0c;所以很多 Python 程序员只用到了其强大功能的一小部分。 今天给小伙伴们分享的这份手册以习题的方式引导读者一步一步学习编程&…

VMware 虚拟机共享宿主机文件夹

一、背景 在虚拟机中&#xff0c;需要写文件到宿主机的文件系统中 宿主机的文件共享给虚拟机使用 这些场景就涉及到VM的虚拟机怎么访问宿主机的磁盘文件夹 二、软件背景 宿主机&#xff1a;window机器&#xff0c;本文是win7 虚拟软件&#xff1a;VMware12.5.6&#xff0…

js文件导出功能

效果图&#xff1a; 代码示例&#xff1a; <!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"><title>html 表格导出道</title><script src"js/jquery-3.6.3.js"></script><st…

Office--加载宏-CS上线

免责声明:本文仅做技术交流与学习... 目录 关于宏上线的格式: 操作流程: 其他office文本的上线格式一样: 关于宏上线的格式: doc宏病毒: 1-生成格式为dotm 只要点启用宏就上线 2-生成格式为()word 97-2003 .doc) 被杀 操作流…

Qt入门小项目 | 实现一个图片查看器

文章目录 一、实现一个图片查看软件 一、实现一个图片查看软件 需要实现的功能&#xff1a; 打开目录选择图片显示图片的名字显示图片 在以上功能的基础上进行优化&#xff0c;需要解决如下问题&#xff1a; 如何记住上次打开的路径&#xff1f; 将路径保存到配置文件中&#x…