整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

记录我首次在Vue中使用D3.js绘制立体直方图的经

记录我首次在Vue中使用D3.js绘制立体直方图的经历

话不多说直接上代码

模板

源数据格式,小编用得是动态数据就不给大家展示了就放个格式给大家看看吧

data=[

{

letter: "白皮鸡蛋",

child: {

category: "0",

value: "459.00"

}

},

{

letter: "红皮鸡蛋",

child: {

category: "0",

value: "389.00"

}

},....]

接下来是重点啊代码有点多(前方高能)

d3在vue中安装也很简单 直接 npm install d3

import * as d3 from "d3";

我是封装在chart函数中的(两个参数分别是源数据和图像挂载dom在vue中直接用this.$refs.cil其他地方的用法和JQ类似)

function chart(data, myEle) {

{

var margin={

top: 20,

right: 50,

bottom: 50,

left: 90

};

var svgWidth=window.screen.width -margin.right -margin.left; // 检测屏幕

var svgHeight=500;

//创建各个面的颜色数组

var mainColorList=[

"#f6e242",

"#ebec5b",

"#d2ef5f",

"#b1d894",

"#97d5ad",

"#82d1c0",

"#70cfd2",

"#63c8ce",

"#50bab8",

"#38a99d"

];

var topColorList=[

"#e9d748",

"#d1d252",

"#c0d75f",

"#a2d37d",

"#83d09e",

"#68ccb6",

"#5bc8cb",

"#59c0c6",

"#3aadab",

"#2da094"

];

var rightColorList=[

"#dfce51",

"#d9db59",

"#b9d54a",

"#9ece7c",

"#8ac69f",

"#70c3b1",

"#65c5c8",

"#57bac0",

"#42aba9",

"#2c9b8f"

];

// console.log(this.$refs.cil)

var svg=d3

.select(myEle)

.append("svg")

.attr("width", svgWidth)

.attr("height", svgHeight)

.attr("id", "svg-column");

// 创建X轴序数比例尺

function addXAxis() {

var transform=d3.geoTransform({

point: function (x, y) {

this.stream.point(x, y);

}

}); //定义几何路径

var path=d3.geoPath().projection(transform);

var xLinearScale=d3

.scaleBand()

.domain(

data.map(function (d) {

return d.letter;

})

)

.range([0, svgWidth - margin.right - margin.left], 0.1);

var xAxis=d3.axisBottom(xLinearScale).ticks(data.length); //绘制X轴

var xAxisG=svg

.append("g")

.call(xAxis)

.attr(

"transform",

"translate(" + margin.left + "," + (svgHeight - margin.bottom) +")"

); //删除原X轴

xAxisG.select("path").remove();

xAxisG.selectAll("line").remove(); //绘制新的立体X轴

xAxisG

.append("path")

.datum({

type: "Polygon",

coordinates: [

[

[20, 0],

[0, 15],

[svgWidth - margin.right - margin.left, 15],

[svgWidth + 20 - margin.right - margin.left, 0],

[20, 0]

]

]

})

.attr("d", path)

.attr("fill", "rgb(187,187,187)");

xAxisG

.selectAll("text")

.attr("fill", "#646464")

.attr("class", 'rotate_txt')

.attr("transform-origin", "50% top 0")

.attr("transform", "translate(0,20)");

dataProcessing(xLinearScale); //核心算法

}

var yLinearScale;

//创建y轴的比例尺渲染y轴

function addYScale() {

yLinearScale=d3

.scaleLinear()

.domain([

0,

d3.max(data, function (d, i) {

return d.child.value * 1;

}) * 1.2

])

.range([svgHeight - margin.top - margin.bottom, 0]);

//定义Y轴比例尺以及刻度

var yAxis=d3.axisLeft(yLinearScale).ticks(6);

//绘制Y轴

var yAxisG=svg

.append("g")

.call(yAxis)

.attr(

"transform",

"translate(" + (margin.left + 10) + "," + margin.top + ")"

);

yAxisG

.selectAll("text")

.attr("font-size", "18px")

.attr("fill", "#636363");

//删除原Y轴路径和tick

yAxisG.select("path").remove();

yAxisG.selectAll("line").remove();

}

//核心算法思路是

function dataProcessing(xLinearScale) {

var angle=Math.PI / 2.3;

for (var i=0; i < data.length; i++) {

var d=data[i];

var depth=10;

d.ow=xLinearScale.bandwidth() * 0.7;

d.ox=xLinearScale(d.letter);

d.oh=1;

d.p1={

x: Math.cos(angle) * d.ow,

y: -Math.sin(angle) - depth

};

d.p2={

x: d.p1.x + d.ow,

y: d.p1.y

};

d.p3={

x: d.p2.x,

y: d.p2.y + d.oh

};

}

}

//tip的创建方法

var tipTimerConfig={

longer: 0,

target: null,

exist: false,

winEvent: window.event,

boxHeight: 398,

boxWidth: 376,

maxWidth: 376,

maxHeight: 398,

tooltip: null,

showTime: 3500,

hoverTime: 300,

displayText: "",

show: function (val, e) {

"use strict";

var me=this;

if (e !=null) {

me.winEvent=e;

}

me.displayText=val;

me.calculateBoxAndShow();

me.createTimer();

},

calculateBoxAndShow: function () {

"use strict";

var me=this;

var _x=0;

var _y=0;

var _w=document.documentElement.scrollWidth;

var _h=document.documentElement.scrollHeight;

var wScrollX=window.scrollX || document.body.scrollLeft;

var wScrollY=window.scrollY || document.body.scrollTop;

var xMouse=me.winEvent.x + wScrollX;

if (_w - xMouse < me.boxWidth) {

_x=xMouse - me.boxWidth - 10;

} else {

_x=xMouse;

}

var _yMouse=me.winEvent.y + wScrollY;

if (_h - _yMouse < me.boxHeight + 18) {

_y=_yMouse - me.boxHeight - 25;

} else {

_y=_yMouse + 18;

}

me.addTooltip(_x, _y);

},

addTooltip: function (page_x, page_y) {

"use strict";

var me=this;

me.tooltip=document.createElement("div");

me.tooltip.style.left=page_x + "px";

me.tooltip.style.top=page_y + "px";

me.tooltip.style.position="absolute";

me.tooltip.style.width=me.boxWidth + "px";

me.tooltip.style.height=me.boxHeight + "px";

me.tooltip.className="three-tooltip";

var divInnerHeader=me.createInner();

divInnerHeader.innerHTML=me.displayText;

me.tooltip.appendChild(divInnerHeader);

document.body.appendChild(me.tooltip);

},

createInner: function () {

"use strict";

var me=this;

var divInnerHeader=document.createElement("div");

divInnerHeader.style.width=me.boxWidth + "px";

divInnerHeader.style.height=me.boxHeight + "px";

return divInnerHeader;

},

ClearDiv: function () {

"use strict";

var delDiv=document.body.getElementsByClassName("three-tooltip");

for (var i=delDiv.length - 1; i >=0; i--) {

document.body.removeChild(delDiv[i]);

}

},

createTimer: function (delTarget) {

"use strict";

var me=this;

var delTip=me.tooltip;

var delTarget=tipTimerConfig.target;

var removeTimer=window.setTimeout(function () {

try {

if (delTip !=null) {

document.body.removeChild(delTip);

if (tipTimerConfig.target==delTarget) {

me.exist=false;

}

}

clearTimeout(removeTimer);

} catch (e) {

clearTimeout(removeTimer);

}

}, me.showTime);

},

hoverTimerFn: function (showTip, showTarget) {

"use strict";

var me=this;

var showTarget=tipTimerConfig.target;

var hoverTimer=window.setInterval(function () {

try {

if (tipTimerConfig.target !=showTarget) {

clearInterval(hoverTimer);

} else if (

!tipTimerConfig.exist &&

new Date().getTime() - me.longer > me.hoverTime

) {

//show

tipTimerConfig.show(showTip);

tipTimerConfig.exist=true;

clearInterval(hoverTimer);

}

} catch (e) {

clearInterval(hoverTimer);

}

}, tipTimerConfig.hoverTime);

}

};

var createTooltipTableData=function (info) {

var ary=[];

ary.push("<div class='tip-hill-div'>");

ary.push("<p>" + info.letter +' '+ info.child.value + "</p>");

ary.push("</div>");

return ary.join("");

};

// 核心算法写完,就到了最终的渲染了

function addColumn() {

function clumnMouseover(d) {

d3.select(this)

.selectAll(".transparentPath")

.attr("opacity", 0.8);

// 添加 div

tipTimerConfig.target=this;

tipTimerConfig.longer=new Date().getTime();

tipTimerConfig.exist=false;

//获取坐标

tipTimerConfig.winEvent={

x: event.clientX - 100,

y: event.clientY

};

tipTimerConfig.boxHeight=50;

tipTimerConfig.boxWidth=140;

//hide

tipTimerConfig.ClearDiv();

//show

tipTimerConfig.hoverTimerFn(createTooltipTableData(d));

}

function clumnMouseout(d) {

d3.select(this)

.selectAll(".transparentPath")

.attr("opacity", 1);

tipTimerConfig.target=null;

tipTimerConfig.ClearDiv();

}

var g=svg

.selectAll(".g")

.data(data)

.enter()

.append("g")

.on("mouseover", clumnMouseover)

.on("mouseout", clumnMouseout)

.attr("transform", function (d) {

return (

"translate(" +

(d.ox + margin.left + 20) +

"," +

(svgHeight - margin.bottom + 15) +

")"

);

});

g.transition()

.duration(2500)

.attr("transform", function (d) {

return (

"translate(" +

(d.ox + margin.left + 20) +

", " +

(yLinearScale(d.child.value) + margin.bottom - 15) +

")"

);

});

g.append("rect")

.attr("x", 0)

.attr("y", 0)

.attr("class", "transparentPath")

.attr("width", function (d, i) {

return d.ow;

})

.attr("height", function (d) {

return d.oh;

})

.style("fill", function (d, i) {

if (!mainColorList[i]) {

return "#38a99d"

}

return mainColorList[i];

})

.transition()

.duration(2500)

.attr("height", function (d, i) {

return (

svgHeight -

margin.bottom -

margin.top -

yLinearScale(d.child.value)

);

});

g.append("path")

.attr("class", "transparentPath")

.attr("d", function (d) {

return (

"M0,0 L" +

d.p1.x +

"," +

d.p1.y +

" L" +

d.p2.x +

"," +

d.p2.y +

" L" +

d.ow +

",0 L0,0"

);

})

.style("fill", function (d, i) {

if (!topColorList[i]) {

return "#2da094"

}

return topColorList[i];

});

g.append("path")

.attr("class", "transparentPath")

.attr("d", function (d) {

return (

"M" +

d.ow +

",0 L" +

d.p2.x +

"," +

d.p2.y +

" L" +

d.p3.x +

"," +

d.p3.y +

" L" +

d.ow +

"," +

d.oh +

" L" +

d.ow +

",0"

);

})

.style("fill", function (d, i) {

if (!rightColorList[i]) {

return "#2c9b8f"

}

return rightColorList[i];

})

.transition()

.duration(2500)

.attr("d", function (d, i) {

return (

"M" +

d.ow +

",0 L" +

d.p2.x +

"," +

d.p2.y +

" L" +

d.p3.x +

"," +

(d.p3.y +

svgHeight -

margin.top -

margin.bottom -

yLinearScale(d.child.value)) +

" L" +

d.ow +

"," +

(svgHeight -

margin.top -

margin.bottom -

yLinearScale(d.child.value)) +

" L" +

d.ow +

",0"

);

});

}

// 函数调用

addXAxis();

addYScale();

addColumn();

}

}

这里提供暴露接口让组件调用

export function showCahrt(data, myEle) {

return chart(data, myEle)

}

这里有个bug我一直没有找到很好的解决办法就是在绘制x轴时文字的样式调整

就是这里使用.attr("transform", "translate(0,20) rotate(-30deg)")也就是旋转加下移组合是根本不生效只能写其中的一个。不知道小伙伴们有没有什么高见很是期待!

整篇的代码有点长其实整体还是很清晰的 。在最后面

有收获的小朋友记得点赞哦

yecharts是一款将python与echarts结合的强大的数据可视化工具,本系列文章将为你阐述pyecharts的使用细则,让你对数据进行可视化处理时更加得心应手。这一系列中全部代码在Windows 10系统下基于Python3.7和pyecharts1.9.0实际运行通过。

一、前言

在本系列上一篇文章中笔者介绍了使用pyecharts绘制地理图表的方法,运用pyecharts在绘制地图方面有着特有的优势,能动态且直观的展现出绘制者想在地图中表达的资源信息。在这一期文章里,笔者将介绍使用pyecharts绘制雷达图、词云图、日历图等基本图表。

二、使用实例

在本期文章中,我们需要导入的库有:

from pyecharts.charts import *
from pyecharts.components import Table
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
import  random
import  datetime

雷达图是一种平行坐标图,是一种从同一点开始的轴上表示多个定量或变量的二维图表。雷达图常用于绘制企业经营状况、展现评价指标之间的关系,能够直观地查看各类数据指标及数据变化趋势。

雷达图示例

实现代码:

data=[
    [66, 91, 123, 78, 82, 67],
    [89, 101, 45, 88, 86, 75],
    [86, 93, 101, 84, 35, 73],
]
radar=(Radar()
         .add_schema(schema=[
             opts.RadarIndicatorItem(name="语文", max_=150),
             opts.RadarIndicatorItem(name="数学", max_=150),
             opts.RadarIndicatorItem(name="英语", max_=150),
             opts.RadarIndicatorItem(name="政治", max_=100),
             opts.RadarIndicatorItem(name="地理", max_=100),
             opts.RadarIndicatorItem(name="历史", max_=100),
         ]
)
    .add('', data)
)
radar.render('雷达图.html')

词云图是一种以各种词汇组成的二维图表,一般用于提取大量文本中的关键字词信息,使读者能一眼扫过词云图便可以明白其主要内容和关键信息,同时也提高了阅读文本的趣味性。

词云图示例

实现代码:

words=[
    ("heart", 173),
    ("no", 365),
    ("you", 360),
    ("can", 282),
    ("yes", 273),
    ("start", 265),
    ("hello", 365),
    ("world", 124),
    ("PPT", 436),
    ("frame", 255),
    ("Hadoop", 666),
    ("zookeeper", 244),
    ("goal", 681),
    ("today", 184),
    ("Monday", 12),
    ("last time", 148),
    ("configuration", 247),
    ("batter", 182),
    ("remember", 255),
    ("to", 150),
    ("make", 162),
    ("her", 266),
    ("in", 60),
    ("my", 82),
]
wc=(
    WordCloud()
    .add("", words)
)
wc.render('词云图.html')

日历图是一种能直观显示出对应日期的某个指标取值的动态图表,利用pyecharts中的Calendar方法绘制日历图十分简单。

日历图示例

实现代码:

begin=datetime.date(2021, 1, 1)
end=datetime.date(2021, 10, 29)
data=[[str(begin + datetime.timedelta(days=i)), random.randint(1000, 1200)]
        for i in range((end - begin).days + 1)]

calendar=(
    Calendar()
        .add("", data,calendar_opts=opts.CalendarOpts(range_="2021"))
)

calendar.render('日历图.html')

饼图用于展现图表中各项的大小与各项总和的比例,常在统计学中应用。

饼图示例

实现代码:

cate=['meituan','zhifubao','weixin','taobao','jingdong','pinduoduo']
data=[123,153,89,107,98,23]
pie=(Pie()
       .add('', [list(z) for z in zip(cate, data)])
       )
pie.render('饼图.html')

仪表盘图是pyecharts中一种特有图表,模仿汽车仪表盘码速表,生动地展示某项数据的取值。

仪表盘图示例

实现代码:

data=(
    '不及格率',
    12
)
gauge=(Gauge()
          .add("", [data])
          )
gauge.render('仪表盘图.html')

水球图同样是pyecharts库中一种特色图表,用于展示某一项指标的取值。

水球图示例

实现代码:

liquid=(Liquid()
          .add("", [0.66, 0.34])
          )
liquid.render('水球图.html')

平行坐标图用于展示在拥有高维度的评价指标情况下,各个评价对象之间的联系和等级。

平行坐标图示例

实现代码:

data=[
    ['301班', 78, 91, 123, 78, 82, 67, "优秀"],
    ['302班', 89, 101, 127, 88, 86, 75, "良好"],
    ['303班', 86, 93, 101, 84, 90, 73, "合格"],
]
parallel=(
    Parallel()
    .add_schema(
        [
            opts.ParallelAxisOpts(
                dim=0,
                name="班级",
                type_="category",
                data=["301班", "302班", "303班"],
            ),
            opts.ParallelAxisOpts(dim=1, name="语文"),
            opts.ParallelAxisOpts(dim=2, name="数学"),
            opts.ParallelAxisOpts(dim=3, name="英语"),
            opts.ParallelAxisOpts(dim=4, name="政治"),
            opts.ParallelAxisOpts(dim=5, name="历史"),
            opts.ParallelAxisOpts(dim=6, name="地理"),
            opts.ParallelAxisOpts(
                dim=7,
                name="等级",
                type_="category",
                data=["优秀", "良好", "合格"],
            ),
        ]
    )
    .add("", data)
)
parallel.render('平行坐标图.html')

旭日图是一种特殊的饼图,能清晰地表达层级和从属关系,在pyecharts中我们使用Sunburst方法绘制。

旭日图示例

实现代码:

data=[
    {"name": "广东",
     "children": [
             {"name": "广州",
              "children": [
                  {"name": "天河区", "value": 55},
                  {"name": "越秀区", "value": 34},
                  {"name": "增城区", "value": 66},
              ]},
             {"name": "东莞",
              "children": [
                      {"name": "麻涌镇", "value": 156},
                      {"name": "望牛墩镇", "value": 134},
              ]},
             {"name": "汕头", "value": 87},
             {"name": "揭阳", "value": 23},
     ],
     },
    {"name": "新疆",
     "children": [
             {"name": "乌鲁木齐",
              "children": [
                  {"name": "五家渠", "value": 55},
                  {"name": "昌吉", "value": 78},
                  {"name": "呼图壁", "value": 34},
              ]},
             {"name": "阿克苏", "value": 67},
             {"name": "克拉玛依", "value": 34},
     ],
     },
    {"name": "重庆", "value": 235}
]
sunburst=(Sunburst()
            .add("", data_pair=data)
            )
sunburst.render('旭日图.html')

河流图是一种特殊的流图,主要用于表示事件或主题在某一时间段内的变化情况。

河流图示例

实现代码:

cate=['meituan','zhifubao','weixin','taobao','jingdong','pinduoduo']
date_list=["2021/10/{}".format(i + 1) for i in range(30)]
data=[[day, random.randint(10, 50), c] for day in date_list for c in cate]
river=(
    ThemeRiver()
    .add(
        series_name=cate,
        data=data,
        singleaxis_opts=opts.SingleAxisOpts(type_="time")
    )
)
river.render('河流图.html')

3D散点图,用于展现三维数据在空间中的分布情况。

3D散点图示例

实现代码:

data=[(random.randint(0, 100), random.randint(0, 100), random.randint(0, 100)) for _ in range(100)]
scatter3D=(Scatter3D()
             .add("", data)
             )
scatter3D.render('3D散点图.html')

3D直方图,用于展示三维数据在空间中的分布情况。

3D直方图示例

实现代码:

data=[[i, j, random.randint(0, 100)] for i in range(24) for j in range(7)]
hour_list=[str(i) for i in range(24)]
week_list=['周日', '周一', '周二', '周三', '周四', '周五', '周六']
bar3D=(
    Bar3D()
    .add(
        "",
        data,
        xaxis3d_opts=opts.Axis3DOpts(hour_list, type_="category"),
        yaxis3d_opts=opts.Axis3DOpts(week_list, type_="category"),
        zaxis3d_opts=opts.Axis3DOpts(type_="value"),
    )
)
bar3D.render_notebook()

以上就是这一期的全部内容,前三期内容笔者介绍了pyecharts库的一些图表的绘制方法,在这一系列的下一篇文章中笔者将介绍pyecharts库中各个函数的配置项,熟练的使用配置项才能创造一个真正属于自己的图表。请各位等待下一期更新。

文分享自华为云社区《[Python从零到壹] 五十四.图像增强及运算篇之局部直方图均衡化和自动色彩均衡化处理-云社区-华为云》,作者: eastmount。

一.局部直方图均衡化

前文通过调用OpenCV中equalizeHist()函数实现直方图均衡化处理,该方法简单高效,但其实它是一种全局意义上的均衡化处理,很多时候这种操作不是很好,会把某些不该调整的部分给均衡处理了。同时,图像中不同的区域灰度分布相差甚远,对它们使用同一种变换常常产生不理想的效果,实际应用中,常常需要增强图像的某些局部区域的细节。

为了解决这类问题,Pizer等提出了局部直方图均衡化的方法(AHE),但AHE方法仅仅考虑了局部区域的像素,忽略了图像其他区域的像素,且对于图像中相似区域具有过度放大噪声的缺点。为此K. Zuiderveld等人提出了对比度受限CLAHE的图像增强方法,通过限制局部直方图的高度来限制局部对比度的增强幅度,从而限制噪声的放大及局部对比度的过增强,该方法常用于图像增强,也可以被用来进行图像去雾操作[1-2]。

在OpenCV中,调用函数createCLAHE()实现对比度受限的局部直方图均衡化。它将整个图像分成许多小块(比如按10×10作为一个小块),那么对每个小块进行均衡化。这种方法主要对于图像直方图不是那么单一的(比如存在多峰情况)图像比较实用。其函数原型如下所示:

retval=createCLAHE([, clipLimit[, tileGridSize]])

  • clipLimit参数表示对比度的大小
  • tileGridSize参数表示每次处理块的大小

调用createCLAHE()实现对比度受限的局部直方图均衡化的代码如下:

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2  
import numpy as np  
import matplotlib.pyplot as plt
 
#读取图片
img=cv2.imread('lena.bmp')

#灰度转换
gray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
#局部直方图均衡化处理
clahe=cv2.createCLAHE(clipLimit=2, tileGridSize=(10,10))

#将灰度图像和局部直方图相关联, 把直方图均衡化应用到灰度图 
result=clahe.apply(gray)

#显示图像
plt.subplot(221)
plt.imshow(gray, cmap=plt.cm.gray), plt.axis("off"), plt.title('(a)') 
plt.subplot(222)
plt.imshow(result, cmap=plt.cm.gray), plt.axis("off"), plt.title('(b)') 
plt.subplot(223)
plt.hist(img.ravel(), 256), plt.title('(c)') 
plt.subplot(224)
plt.hist(result.ravel(), 256), plt.title('(d)') 
plt.show()

输出结果如图1所示,图1(a)为原始图像,对应的直方图为图1?,图1(b)和图1(d)为对比度受限的局部直方图均衡化处理后的图像及对应直方图,它让图像的灰度值分布更加均衡。可以看到,相对于全局的直方图均衡化,这个局部的均衡化似乎得到的效果更自然一点。

二.自动色彩均衡化

Retinex算法是代表性的图像增强算法,它根据人的视网膜和大脑皮层模拟对物体颜色的波长光线反射能力而形成,对复杂环境下的一维条码具有一定范围内的动态压缩,对图像边缘有着一定自适应的增强。自动色彩均衡(Automatic Color Enhancement,ACE)算法是在Retinex算法的理论上提出的,它通过计算图像目标像素点和周围像素点的明暗程度及其关系来对最终的像素值进行校正,实现图像的对比度调整,产生类似人体视网膜的色彩恒常性和亮度恒常性的均衡,具有很好的图像增强效果[3-4]。

ACE算法包括两个步骤,一是对图像进行色彩和空域调整,完成图像的色差校正,得到空域重构图像;二是对校正后的图像进行动态扩展。ACE算法计算公式如下:

其中,W是权重参数,离中心点像素越远的W值越小;g是相对对比度调节参数,其计算方法如公式(22-2)所示,a表示控制参数,值越大细节增强越明显。

图2是条形码图像进行ACE图像增强后的效果图,通过图像增强后的图(b)对比度更强,改善了原图像的明暗程度,增强的同时保持了图像的真实性。

由于OpenCV中暂时没有ACE算法包,下面的代码是借鉴“zmshy2128”老师的文章,修改实现的彩色直方图均衡化处理[5]。

# -*- coding: utf-8 -*-
# By:Eastmount
# 参考zmshy2128老师文章
import cv2
import numpy as np
import math
import matplotlib.pyplot as plt

#线性拉伸处理
#去掉最大最小0.5%的像素值 线性拉伸至[0,1]
def stretchImage(data, s=0.005, bins=2000):   
    ht=np.histogram(data, bins);
    d=np.cumsum(ht[0])/float(data.size)
    lmin=0; lmax=bins-1
    while lmin<bins:
        if d[lmin]>=s:
            break
        lmin+=1
    while lmax>=0:
        if d[lmax]<=1-s:
            break
        lmax-=1
    return np.clip((data-ht[1][lmin])/(ht[1][lmax]-ht[1][lmin]), 0,1)

#根据半径计算权重参数矩阵
g_para={}
def getPara(radius=5):                        
    global g_para
    m=g_para.get(radius, None)
    if m is not None:
        return m
    size=radius*2+1
    m=np.zeros((size, size))
    for h in range(-radius, radius+1):
        for w in range(-radius, radius+1):
            if h==0 and w==0:
                continue
            m[radius+h, radius+w]=1.0/math.sqrt(h**2+w**2)
    m /=m.sum()
    g_para[radius]=m
    return m

#常规的ACE实现
def zmIce(I, ratio=4, radius=300):                     
    para=getPara(radius)
    height,width=I.shape
    
    #Python3报错如下 使用列表append修改
    zh=[]
    zw=[]
    n=0
    while n < radius:
        zh.append(0)
        zw.append(0)
        n +=1
    for n in range(height):
        zh.append(n)
    for n in range(width):
        zw.append(n)
    n=0
    while n < radius:
        zh.append(height-1)
        zw.append(width-1)
        n +=1
    #print(zh)
    #print(zw)
    
    Z=I[np.ix_(zh, zw)]
    res=np.zeros(I.shape)
    for h in range(radius*2+1):
        for w in range(radius*2+1):
            if para[h][w]==0:
                continue
            res +=(para[h][w] * np.clip((I-Z[h:h+height, w:w+width])*ratio, -1, 1))
    return res

#单通道ACE快速增强实现
def zmIceFast(I, ratio, radius):
    print(I)
    height, width=I.shape[:2]
    if min(height, width) <=2:
        return np.zeros(I.shape)+0.5
    Rs=cv2.resize(I, (int((width+1)/2), int((height+1)/2)))
    Rf=zmIceFast(Rs, ratio, radius)             #递归调用
    Rf=cv2.resize(Rf, (width, height))
    Rs=cv2.resize(Rs, (width, height))
 
    return Rf+zmIce(I,ratio, radius)-zmIce(Rs,ratio,radius)   

#rgb三通道分别增强 ratio是对比度增强因子 radius是卷积模板半径          
def zmIceColor(I, ratio=4, radius=3):               
    res=np.zeros(I.shape)
    for k in range(3):
        res[:,:,k]=stretchImage(zmIceFast(I[:,:,k], ratio, radius))
    return res

#主函数
if __name__=='__main__':
    img=cv2.imread('test01.png')
    res=zmIceColor(img/255.0)*255
    cv2.imwrite('Ice.jpg', res)

运行结果如图3和图4所示,ACE算法能有效进行图像去雾处理,实现图像的细节增强。

三.总结

本文主要讲解图像局部直方图均衡化和自动色彩均衡化处理。这些算法可以广泛应用于图像增强、图像去噪、图像去雾等领域。

参考文献:

  • [1]王浩,张叶,沈宏海,张景忠.图像增强算法综述[J].中国光学,2017,10(04):438-448.
  • [2]李艳梅. 图像增强的相关技术及应用研究[D].电子科技大学,2013.
  • [3]S. Bidon, Olivier Besson, J. Y. Tourneret. The Adaptive Coherence Estimator is the Generalized Likelihood Ratio Test for a Class of Heterogeneous Environments[J]. IEEE Signal Processing Letters, 2008, 15: 281-284.
  • [4]eastmount. [Python图像处理] 三十八.OpenCV图像增强和图像去雾万字详解(直方图均衡化、局部直方图均衡化、自动色彩均衡化)[EB/OL]. (2021-03-12). https://blog.csdn.net/ Eastmount/article/details/114706950.
  • [5]zmshy2128. 自动色彩均衡(ACE)快速算法[EB/OL]. (2016-12-05). https://www.cnblogs.com/zmshy2128/p/6135551.html.

点击下方,第一时间了解华为云新鲜技术~

华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云