使用 Open CV 更改照片中的 Powerpoint 幻灯片
Posted
技术标签:
【中文标题】使用 Open CV 更改照片中的 Powerpoint 幻灯片【英文标题】:Changing Powerpoint Slide in Photo using Open CV 【发布时间】:2015-09-22 12:58:29 【问题描述】:如何使用 OpenCV 或合适的图像处理工具自动更改照片中的幻灯片以变得更好?我想它需要检测投影仪,自动对比颜色,透视变形以变得更好。
我是用photoshop手动转换的
到
在 Photoshop 中使用 1)自动对比 2)透视变形
【问题讨论】:
您是否还需要自动分割海报,或者您可以提供顶点作为输入? 如果您编码,您可以使用 OpenCV 来完成,还有 warp 功能,您可以在线获得大量教程。对比度和图像调整是 OpenCV 中的小菜一碟。如果您不太了解编码,请使用 python 选择 OpenCV。 @Miki,是的,我需要自动分割海报,我觉得这是最困难的部分,我应该如何实现? @PervezAlam 我需要使用哪些具体功能来分割海报并执行自动对比?如果需要,我可以编写代码。 只要谷歌,有很多可用的教程。例如swarthmore.edu/NatSci/mzucker1/opencv-2.4.10-docs/doc/tutorials/… 要获取幻灯片的顶点,您可以使用霍夫或边缘检测,然后找到交点。对比度调整也是如此。 【参考方案1】:你可以:
1) 在几乎是蓝色的颜色上对 HSV 图像进行阈值处理以获得海报蒙版:
2) 找到外部线,以及它们的交点:
3) 应用透视变换:
4) 应用一些颜色增强。这里我使用了 Matlab imadjust 的等价物。有关在 OpenCV 中的移植,请参阅 here。
这里是完整的代码。 cmets 应阐明每个步骤。如果有什么不清楚的地方请告诉我。
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void imadjust(const Mat1b& src, Mat1b& dst, int tol = 1, Vec2i in = Vec2i(0, 255), Vec2i out = Vec2i(0, 255))
// src : input CV_8UC1 image
// dst : output CV_8UC1 imge
// tol : tolerance, from 0 to 100.
// in : src image bounds
// out : dst image buonds
dst = src.clone();
tol = max(0, min(100, tol));
if (tol > 0)
// Compute in and out limits
// Histogram
vector<int> hist(256, 0);
for (int r = 0; r < src.rows; ++r)
for (int c = 0; c < src.cols; ++c)
hist[src(r, c)]++;
// Cumulative histogram
vector<int> cum = hist;
for (int i = 1; i < hist.size(); ++i)
cum[i] = cum[i - 1] + hist[i];
// Compute bounds
int total = src.rows * src.cols;
int low_bound = total * tol / 100;
int upp_bound = total * (100 - tol) / 100;
in[0] = distance(cum.begin(), lower_bound(cum.begin(), cum.end(), low_bound));
in[1] = distance(cum.begin(), lower_bound(cum.begin(), cum.end(), upp_bound));
// Stretching
float scale = float(out[1] - out[0]) / float(in[1] - in[0]);
for (int r = 0; r < dst.rows; ++r)
for (int c = 0; c < dst.cols; ++c)
int vs = max(src(r, c) - in[0], 0);
int vd = min(int(vs * scale + 0.5f) + out[0], out[1]);
dst(r, c) = saturate_cast<uchar>(vd);
int main()
// Load image
Mat3b img = imread("path_to_image");
Mat3b dbg = img.clone(); // Debug image
// Convert to HSV
Mat3b hsv;
cvtColor(img, hsv, COLOR_BGR2HSV);
// Threshold on HSV values
Mat1b mask;
inRange(hsv, Scalar(100, 140, 120), Scalar(110, 170, 200), mask);
// Get the external boundaries
Mat1b top(mask.rows, mask.cols, uchar(0));
Mat1b bottom(mask.rows, mask.cols, uchar(0));
Mat1b left(mask.rows, mask.cols, uchar(0));
Mat1b right(mask.rows, mask.cols, uchar(0));
for (int r = 0; r < mask.rows; ++r)
// Find first in row
for (int c = 0; c < mask.cols; ++c)
if (mask(r, c))
left(r, c) = 255;
break;
// Find last in row
for (int c = mask.cols - 1; c >= 0; --c)
if (mask(r, c))
right(r, c) = 255;
break;
for (int c = 0; c < mask.cols; ++c)
// Find first in col
for (int r = 0; r < mask.rows; ++r)
if (mask(r, c))
top(r, c) = 255;
break;
// Find last in col
for (int r = mask.rows - 1; r >= 0; --r)
if (mask(r, c))
bottom(r, c) = 255;
break;
// Find lines
vector<Vec2f> linesTop, linesBottom, linesLeft, linesRight;
HoughLines(top, linesTop, 1, CV_PI / 180.0, 100);
HoughLines(bottom, linesBottom, 1, CV_PI / 180.0, 100);
HoughLines(left, linesLeft, 1, CV_PI / 180.0, 100);
HoughLines(right, linesRight, 1, CV_PI / 180.0, 100);
// Find intersections
Mat1b maskLines(mask.rows, mask.cols, uchar(0));
if (linesTop.empty() || linesBottom.empty() || linesLeft.empty() || linesRight.empty())
cout << "No enough lines detected" << endl;
return -1;
// Keep only the first line detected for each side
vector<Vec2f> lines linesTop[0], linesBottom[0], linesLeft[0], linesRight[0] ;
for (size_t i = 0; i < lines.size(); i++)
float rho = lines[i][0], theta = lines[i][1];
// Get 2 points on each line
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
// Draw lines
Mat1b maskCurrentLine(mask.rows, mask.cols, uchar(0));
line(maskCurrentLine, pt1, pt2, Scalar(1), 1);
maskLines += maskCurrentLine;
line(dbg, pt1, pt2, Scalar(0, 0, 255), 3, CV_AA);
// Keep only intersections
maskLines = maskLines > 1;
// Get ordered set of vertices
vector<Point2f> vertices;
// Top left
Mat1b tl(maskLines(Rect(0, 0, mask.cols / 2, mask.rows / 2)));
for (int r = 0; r < tl.rows; ++r)
for (int c = 0; c < tl.cols; ++c)
if (tl(r, c))
vertices.push_back(Point2f(c, r));
// Top right
Mat1b tr(maskLines(Rect(mask.cols / 2, 0, mask.cols / 2, mask.rows / 2)));
for (int r = 0; r < tr.rows; ++r)
for (int c = 0; c < tr.cols; ++c)
if (tr(r, c))
vertices.push_back(Point2f(mask.cols / 2 + c, r));
// Bottom right
Mat1b br(maskLines(Rect(mask.cols / 2, mask.rows / 2, mask.cols / 2, mask.rows / 2)));
for (int r = 0; r < br.rows; ++r)
for (int c = 0; c < br.cols; ++c)
if (br(r, c))
vertices.push_back(Point2f(mask.cols / 2 + c, mask.rows / 2 + r));
// Bottom left
Mat1b bl(maskLines(Rect(0, mask.rows / 2, mask.cols / 2, mask.rows / 2)));
for (int r = 0; r < bl.rows; ++r)
for (int c = 0; c < bl.cols; ++c)
if (bl(r, c))
vertices.push_back(Point2f(c, mask.rows / 2 + r));
// Draw vertices
for (int i = 0; i < vertices.size(); ++i)
circle(dbg, vertices[i], 7, Scalar(0,255,0), CV_FILLED);
// Init output image
Mat3b result(img.rows, img.cols, Vec3b(0, 0, 0));
// Output vertices
vector<Point2f> verticesOut = Point2f(0, 0), Point2f(img.cols, 0), Point2f(img.cols, img.rows), Point2f(0, img.rows) ;
// Get transformation
Mat M = getPerspectiveTransform(vertices, verticesOut);
warpPerspective(img, result, M, result.size());
// Imadjust
vector<Mat1b> planes;
split(result, planes);
for (int i = 0; i < planes.size(); ++i)
imadjust(planes[i], planes[i]);
Mat3b adjusted;
merge(planes, adjusted);
imshow("Result", result);
imshow("Adjusted", adjusted);
waitKey();
return 0;
【讨论】:
以上是关于使用 Open CV 更改照片中的 Powerpoint 幻灯片的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Open CV 3 python 3 用鼠标从大图像中裁剪 ROI
Open CV 计算机视觉中的 haar 级联分类器里面有啥?