扫描乐谱中的五线谱、符干和其他直线具有明显方向。与其先寻找复杂的识别模型,不如让一个细长的结构元素沿指定方向“筛选”像素。OpenCV 教程用腐蚀和膨胀分别提取水平与垂直结构,再对粗糙边缘做局部平滑,展示了一个完整的形态学图像处理流程。
本文依据 OpenCV 4.13.0 的 Extract horizontal and vertical lines by using morphological operations 全文整理。原作者为 Theodore Tsesmelis,C++ 示例源码署名 OpenCV team。原教程标注适用于 OpenCV ≥ 3.0;本文按固定 4.13.0 文档及同名仓库标签核对 C++、Java、Python 三份实现,核对日期为 2026 年 10 月 5 日。
教程的目标是用 erode、dilate 和 getStructuringElement 从乐谱中分离横竖线。输出是线条图与平滑后的图像,没有完成音高、节奏、音符类别或 OCR 识别。这里没有运行图像处理代码;原创算法示意图与原教程结果图分别标注,不冒充本次实测输出。

形态学操作比较的是核覆盖范围中的像素
形态学是一组以结构元素为依据的图像操作。对于输出图中的每个像素,算法查看输入图中相应位置及其邻域,再根据核的形状与大小计算结果。选择不同结构元素,就能让操作对特定形状更敏感。
膨胀取结构元素覆盖的邻域像素最大值。二值图中,只要核所覆盖的有效位置有一个前景像素,输出就成为前景;以白色为前景时,它表现为对象边界向外扩张。腐蚀则取邻域最小值,白色前景会缩小,窄小或不连续结构可能被去掉。原教程分别展示二值和灰度示例;对多通道图像,这类数值运算按通道处理,不能把“颜色最大”理解为人眼感知的最亮颜色。
结构元素是由 0 和 1 表示的邻域形状,1 对应参与运算的位置。核通常远小于原图,锚点决定当前被处理像素对应核中的哪个位置。常见形状包括直线、菱形、圆盘与周期性线条。原文用 7×7 菱形展示中心锚点;本例则采用两个矩形核,一个横向细长,一个纵向细长。核越接近想保留对象的方向与尺度,筛选效果就越有针对性。
以下原教程图像保持原始像素;原文的 src.png 与 gray.png 内容一致,合并展示并保留两个阶段的说明。本文原创算法示意图另行标注。





准备输入与运行环境
输入应使用教程自身的乐谱图 src.png(固定 4.13.0 标签)。原网页的下载链接指向会继续变化的 4.x 分支,因此这里给出固定标签,便于与代码核对。不要随便换一张类似乐谱后声称复现了原例,也不要假设 Python 轮子会自动带齐教程图片。
本文采用的 Python 复现基线是 CPython 3.11、opencv-python==4.13.0.92 与 numpy==2.2.6,使用 CPU 标准包。它是待实际复现的环境约定,本次未安装这些依赖。标准、contrib、headless 包共用 cv2 命名空间,一般应选一种;本文代码调用 imshow,需要图形会话,不能直接把 headless 包当作等价替换。可用轮子仍取决于 Python、操作系统和处理器架构,包的最低 Python 元数据并不意味着每个平台都有预编译文件。
python -m pip install "opencv-python==4.13.0.92" "numpy==2.2.6"
python morph_lines_detection.py /path/to/src.png
C++ 版本需要对应的 OpenCV 头文件和库;Java 版本需要相匹配的 Java 绑定及本地库。三个版本都逐阶段打开窗口,等待按键后销毁该阶段窗口。远程无桌面的机器需要另行设计保存图像的输出方式,本文没有把 GUI 样例伪装成无界面服务。
从输入到二值图
读取并检查图像
Python 用 cv.imread(path, cv.IMREAD_COLOR) 读取输入,失败时返回 None;C++ 与 Java 检查 Mat.empty()。C++ 原例使用 CommandLineParser 与 samples::findFile,默认名称是 notes.png,但网页提供的示例图叫 src.png。因此应明确传入图片路径,而不是依赖文件名恰好存在。
以颜色方式读取会得到 BGR 图。三个版本随后将三通道输入转换为灰度;如果本来已经是灰度,则直接使用。显示阶段分别命名为 src 与 gray。原例的灰度分支是教学性防守写法,并不意味着 IMREAD_COLOR 会保留原输入的单通道形式。

反色后做自适应阈值
黑字白底的乐谱先反色,让希望保留的线条成为亮前景,再执行局部均值阈值:
gray = cv.bitwise_not(gray)
bw = cv.adaptiveThreshold(
gray, 255, cv.ADAPTIVE_THRESH_MEAN_C,
cv.THRESH_BINARY, 15, -2
)
最大输出值为 255;局部窗口是 15×15,方法为邻域均值,常量 C 为 -2。OpenCV 的阈值为局部均值减去 C,所以这里相当于均值加 2;不能把负号抄丢。自适应阈值使用 8 位单通道输入,窗口大小要求是大于 1 的奇数。参数针对原例选定,不是所有扫描分辨率与光照条件的通用答案。

把横向与纵向处理分开
从二值图复制出两个独立缓冲区 horizontal 与 vertical。C++ 与 Java 使用 clone();Python 使用 np.copy()。不能把两个变量都简单指向同一可变数组,再以为它们是彼此独立的输出。
水平结构元素
原例把图像宽度整数除以 30,得到水平核的长度,高度固定为 1:
horizontal_size = horizontal.shape[1] // 30
horizontal_structure = cv.getStructuringElement(
cv.MORPH_RECT, (horizontal_size, 1)
)
horizontal = cv.erode(horizontal, horizontal_structure)
horizontal = cv.dilate(horizontal, horizontal_structure)
先腐蚀、后用同一个核膨胀,是开运算的组合。第一步淘汰在水平方向上不足以容纳整个核的细小结构;第二步让保留下来的结构重新延展。它通常保留较长的横线,并抑制不符合这个方向和尺度的对象,而不是无条件保留“所有水平像素”。


垂直结构元素
垂直分支同样用图像高度整数除以 30,核宽为 1:
vertical_size = vertical.shape[0] // 30
vertical_structure = cv.getStructuringElement(
cv.MORPH_RECT, (1, vertical_size)
)
vertical = cv.erode(vertical, vertical_structure)
vertical = cv.dilate(vertical, vertical_structure)
两个核都使用 OpenCV 的宽、高顺序,而 NumPy 的 shape 是行、高度在前,列、宽度在后。把顺序混淆,会把希望提取的方向反过来。图像任一相关尺寸小于 30 时,整数除法还会得到 0,导致无效核;本文配套代码增加了输入尺寸检查,但实际核长仍需根据扫描分辨率、线宽、倾斜和对象尺寸调整。
横线与竖线图是两个独立结果。后续平滑处理沿着垂直分支进行;原例没有把水平结果与垂直结果合并为完整的音符识别模型,也没有做乐谱几何校正。旋转或透视变形明显时,轴对齐核的效果会下降。


细化边缘:只在边界附近替换为平滑值
原教程观察到提取后的音符相关结构边缘较粗糙,于是把 vertical 反色,再用一个更小的局部阈值窗口产生边缘区域掩膜。这里的“提边”是该教程采用的阈值技巧,并非调用 Canny。
完整处理顺序为:反色垂直结果;用 3×3 自适应阈值找出边缘区域;用 2×2 全 1 核膨胀掩膜;复制垂直图并做 2×2 均值模糊;最后只在掩膜非零位置,将原图像素替换为平滑结果。
vertical = cv.bitwise_not(vertical)
edges = cv.adaptiveThreshold(
vertical, 255, cv.ADAPTIVE_THRESH_MEAN_C,
cv.THRESH_BINARY, 3, -2
)
edges = cv.dilate(edges, np.ones((2, 2), np.uint8))
smooth = cv.blur(np.copy(vertical), (2, 2))
rows, cols = np.where(edges != 0)
vertical[rows, cols] = smooth[rows, cols]
C++ 和 Java 用 smooth.copyTo(vertical, edges) 完成带掩膜复制,Python 用 np.where 取出同一组坐标。最终不是把整幅图都替换成模糊图,非掩膜位置保持不变。这有助于在平滑边缘时保留其他区域,但模糊之后像素不一定仍只有 0 与 255,因此不能自动把最终输出当成严格二值图。

三种语言的完整程序
以下三份程序都保留原例的全部处理阶段及窗口显示。为明确失败行为,均新增“宽和高至少为 30”的检查;Python 入口改为把 main 返回值传给进程退出状态;C++ 显式补上 imgcodecs.hpp 头文件。除这些已标明的静态修订外,算法、阈值、核尺寸规则与最终掩膜复制流程保持原样。本次没有运行或编译它们。
Python
"""
@file morph_lines_detection.py
@brief Use morphology transformations for extracting horizontal and vertical lines sample code
"""
import numpy as np
import sys
import cv2 as cv
def show_wait_destroy(winname, img):
cv.imshow(winname, img)
cv.moveWindow(winname, 500, 0)
cv.waitKey(0)
cv.destroyWindow(winname)
def main(argv):
# [load_image]
# Check number of arguments
if len(argv) < 1:
print ('Not enough parameters')
print ('Usage:\nmorph_lines_detection.py < path_to_image >')
return -1
# Load the image
src = cv.imread(argv[0], cv.IMREAD_COLOR)
# Check if image is loaded fine
if src is None:
print ('Error opening image: ' + argv[0])
return -1
# 中文整理新增:防止尺寸除以 30 后得到零尺寸核。
if min(src.shape[:2]) < 30:
print("Image width and height must be at least 30 pixels")
return 1
# Show source image
cv.imshow("src", src)
# [load_image]
# [gray]
# Transform source image to gray if it is not already
if len(src.shape) != 2:
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
else:
gray = src
# Show gray image
show_wait_destroy("gray", gray)
# [gray]
# [bin]
# Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
gray = cv.bitwise_not(gray)
bw = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
cv.THRESH_BINARY, 15, -2)
# Show binary image
show_wait_destroy("binary", bw)
# [bin]
# [init]
# Create the images that will use to extract the horizontal and vertical lines
horizontal = np.copy(bw)
vertical = np.copy(bw)
# [init]
# [horiz]
# Specify size on horizontal axis
cols = horizontal.shape[1]
horizontal_size = cols // 30
# Create structure element for extracting horizontal lines through morphology operations
horizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))
# Apply morphology operations
horizontal = cv.erode(horizontal, horizontalStructure)
horizontal = cv.dilate(horizontal, horizontalStructure)
# Show extracted horizontal lines
show_wait_destroy("horizontal", horizontal)
# [horiz]
# [vert]
# Specify size on vertical axis
rows = vertical.shape[0]
verticalsize = rows // 30
# Create structure element for extracting vertical lines through morphology operations
verticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))
# Apply morphology operations
vertical = cv.erode(vertical, verticalStructure)
vertical = cv.dilate(vertical, verticalStructure)
# Show extracted vertical lines
show_wait_destroy("vertical", vertical)
# [vert]
# [smooth]
# Inverse vertical image
vertical = cv.bitwise_not(vertical)
show_wait_destroy("vertical_bit", vertical)
'''
Extract edges and smooth image according to the logic
1. extract edges
2. dilate(edges)
3. src.copyTo(smooth)
4. blur smooth img
5. smooth.copyTo(src, edges)
'''
# Step 1
edges = cv.adaptiveThreshold(vertical, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
cv.THRESH_BINARY, 3, -2)
show_wait_destroy("edges", edges)
# Step 2
kernel = np.ones((2, 2), np.uint8)
edges = cv.dilate(edges, kernel)
show_wait_destroy("dilate", edges)
# Step 3
smooth = np.copy(vertical)
# Step 4
smooth = cv.blur(smooth, (2, 2))
# Step 5
(rows, cols) = np.where(edges != 0)
vertical[rows, cols] = smooth[rows, cols]
# Show final result
show_wait_destroy("smooth - final", vertical)
# [smooth]
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
C++
/**
* @file Morphology_3(Extract_Lines).cpp
* @brief Use morphology transformations for extracting horizontal and vertical lines sample code
* @author OpenCV team
*/
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>
void show_wait_destroy(const char* winname, cv::Mat img);
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
//! [load_image]
CommandLineParser parser(argc, argv, "{@input | notes.png | input image}");
Mat src = imread( samples::findFile( parser.get<String>("@input") ), IMREAD_COLOR);
if (src.empty())
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
// 中文整理新增:避免零尺寸结构元素。
if (src.cols < 30 || src.rows < 30) {
std::cerr << "Image width and height must be at least 30 pixels\n";
return 1;
}
// Show source image
imshow("src", src);
//! [load_image]
//! [gray]
// Transform source image to gray if it is not already
Mat gray;
if (src.channels() == 3)
{
cvtColor(src, gray, COLOR_BGR2GRAY);
}
else
{
gray = src;
}
// Show gray image
show_wait_destroy("gray", gray);
//! [gray]
//! [bin]
// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
Mat bw;
adaptiveThreshold(~gray, bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
// Show binary image
show_wait_destroy("binary", bw);
//! [bin]
//! [init]
// Create the images that will use to extract the horizontal and vertical lines
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
//! [init]
//! [horiz]
// Specify size on horizontal axis
int horizontal_size = horizontal.cols / 30;
// Create structure element for extracting horizontal lines through morphology operations
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontal_size, 1));
// Apply morphology operations
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
// Show extracted horizontal lines
show_wait_destroy("horizontal", horizontal);
//! [horiz]
//! [vert]
// Specify size on vertical axis
int vertical_size = vertical.rows / 30;
// Create structure element for extracting vertical lines through morphology operations
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, vertical_size));
// Apply morphology operations
erode(vertical, vertical, verticalStructure, Point(-1, -1));
dilate(vertical, vertical, verticalStructure, Point(-1, -1));
// Show extracted vertical lines
show_wait_destroy("vertical", vertical);
//! [vert]
//! [smooth]
// Inverse vertical image
bitwise_not(vertical, vertical);
show_wait_destroy("vertical_bit", vertical);
// Extract edges and smooth image according to the logic
// 1. extract edges
// 2. dilate(edges)
// 3. src.copyTo(smooth)
// 4. blur smooth img
// 5. smooth.copyTo(src, edges)
// Step 1
Mat edges;
adaptiveThreshold(vertical, edges, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
show_wait_destroy("edges", edges);
// Step 2
Mat kernel = Mat::ones(2, 2, CV_8UC1);
dilate(edges, edges, kernel);
show_wait_destroy("dilate", edges);
// Step 3
Mat smooth;
vertical.copyTo(smooth);
// Step 4
blur(smooth, smooth, Size(2, 2));
// Step 5
smooth.copyTo(vertical, edges);
// Show final result
show_wait_destroy("smooth - final", vertical);
//! [smooth]
return 0;
}
void show_wait_destroy(const char* winname, cv::Mat img) {
imshow(winname, img);
moveWindow(winname, 500, 0);
waitKey(0);
destroyWindow(winname);
}
Java
/**
* @file Morphology_3.java
* @brief Use morphology transformations for extracting horizontal and vertical lines sample code
*/
import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
class Morphology_3Run {
public void run(String[] args) {
//! [load_image]
// Check number of arguments
if (args.length == 0){
System.out.println("Not enough parameters!");
System.out.println("Program Arguments: [image_path]");
System.exit(-1);
}
// Load the image
Mat src = Imgcodecs.imread(args[0]);
// Check if image is loaded fine
if( src.empty() ) {
System.out.println("Error opening image: " + args[0]);
System.exit(-1);
}
// 中文整理新增:避免零尺寸结构元素。
if (src.cols() < 30 || src.rows() < 30) {
System.err.println("Image width and height must be at least 30 pixels");
System.exit(1);
}
// Show source image
HighGui.imshow("src", src);
//! [load_image]
//! [gray]
// Transform source image to gray if it is not already
Mat gray = new Mat();
if (src.channels() == 3)
{
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
}
else
{
gray = src;
}
// Show gray image
showWaitDestroy("gray" , gray);
//! [gray]
//! [bin]
// Apply adaptiveThreshold at the bitwise_not of gray
Mat bw = new Mat();
Core.bitwise_not(gray, gray);
Imgproc.adaptiveThreshold(gray, bw, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 15, -2);
// Show binary image
showWaitDestroy("binary" , bw);
//! [bin]
//! [init]
// Create the images that will use to extract the horizontal and vertical lines
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
//! [init]
//! [horiz]
// Specify size on horizontal axis
int horizontal_size = horizontal.cols() / 30;
// Create structure element for extracting horizontal lines through morphology operations
Mat horizontalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(horizontal_size,1));
// Apply morphology operations
Imgproc.erode(horizontal, horizontal, horizontalStructure);
Imgproc.dilate(horizontal, horizontal, horizontalStructure);
// Show extracted horizontal lines
showWaitDestroy("horizontal" , horizontal);
//! [horiz]
//! [vert]
// Specify size on vertical axis
int vertical_size = vertical.rows() / 30;
// Create structure element for extracting vertical lines through morphology operations
Mat verticalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size( 1,vertical_size));
// Apply morphology operations
Imgproc.erode(vertical, vertical, verticalStructure);
Imgproc.dilate(vertical, vertical, verticalStructure);
// Show extracted vertical lines
showWaitDestroy("vertical", vertical);
//! [vert]
//! [smooth]
// Inverse vertical image
Core.bitwise_not(vertical, vertical);
showWaitDestroy("vertical_bit" , vertical);
// Extract edges and smooth image according to the logic
// 1. extract edges
// 2. dilate(edges)
// 3. src.copyTo(smooth)
// 4. blur smooth img
// 5. smooth.copyTo(src, edges)
// Step 1
Mat edges = new Mat();
Imgproc.adaptiveThreshold(vertical, edges, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 3, -2);
showWaitDestroy("edges", edges);
// Step 2
Mat kernel = Mat.ones(2, 2, CvType.CV_8UC1);
Imgproc.dilate(edges, edges, kernel);
showWaitDestroy("dilate", edges);
// Step 3
Mat smooth = new Mat();
vertical.copyTo(smooth);
// Step 4
Imgproc.blur(smooth, smooth, new Size(2, 2));
// Step 5
smooth.copyTo(vertical, edges);
// Show final result
showWaitDestroy("smooth - final", vertical);
//! [smooth]
System.exit(0);
}
private void showWaitDestroy(String winname, Mat img) {
HighGui.imshow(winname, img);
HighGui.moveWindow(winname, 500, 0);
HighGui.waitKey(0);
HighGui.destroyWindow(winname);
}
}
public class Morphology_3 {
public static void main(String[] args) {
// Load the native library.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
new Morphology_3Run().run(args);
}
}
如何逐步核对结果
运行原例时,应依次观察 src、gray、binary、horizontal、vertical、vertical_bit、edges、dilate 和 smooth - final。如果二值图已经漏掉细线,后续形态学操作不会恢复原本丢失的信息;如果横线图混入大量非目标对象,应先检查方向和核长;如果垂直结构过度消失,应检查高度尺度与阈值,而不是只加大最后的模糊。
读取外部图像时还应控制文件来源、像素尺寸和内存消耗;示例的 None / empty() 检查只覆盖读入失败,不代表已完成不可信文件处理的完整防护。这里没有网络请求、系统命令或凭据,但不能据此宣称代码“没有漏洞”。
原教程的理论图、乐谱原图和各阶段结果图已从 OpenCV 4.13.0 固定标签保留。它们是原作者提供的图像,并非本次运行结果。
原作者:Theodore Tsesmelis;相关代码:OpenCV team 及贡献者。OpenCV 4.13.0 仓库根许可为 Apache License 2.0,完整许可文本附于文末,源码来源保留;不把仓库总许可自动扩大为所有第三方图片或视频的许可。中文翻译及编辑标注:未完纪,2026-10-05。
版权与许可全文
以下保留本页涉及的来源材料或示例代码的版权、许可条件与免责声明;各自适用范围依原声明。中文翻译及编辑标注:未完纪,2026-10-05。
LICENSE
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.












暂无评论内容