如何确定在图像中放置文本的最佳位置(背景)

How to generally determine the best place (background) to put text in an image

ORIGINAL PHOTO

Masked photo.

对原始图像进行遮罩后,我的图像上方有一个黑色遮罩区域。现在我想实现矩形边界并将文本实现到矩形的中心点。那我就拿点东西吧。

This is what I expected.

def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_canny = cv2.Canny(img_gray, 0, 50)
img_dilate = cv2.dilate(img_canny, None, iterations=1)
img_erode = cv2.erode(img_dilate, None, iterations=1)
return img_erode
def get_masked(img):
h, w, _ = img.shape
center = h // 2, w // 2
contours, _ = cv2.findContours(process(img), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
    if cv2.contourArea(cnt) > 100:
        if cv2.pointPolygonTest(cnt, center, False) > 0:
            mask = np.zeros((h, w), 'uint8')
            cv2.drawContours(mask, [cnt], -1, 255, -1)
            return cv2.bitwise_and(img, img, mask=mask)

现在我有了这个获取黑色区域的代码,下一步我该怎么做?

运行一个distance transform on your black area mask. Take the highest value from that distance transform and use that location as the center of your text. (you will want to make sure to put an edge around the image so that the distance transform pushes away from the boundaries of the image as well). What this will do is find the black area furthest away from any non-background pixels. You can use minMaxLoc opencv函数求距离t运行sform.

后最大值的位置

这会将文本放在图片的任何位置(完全基于您的背景遮罩/图像边缘)。如果你想强制执行某种限制(比如背景但只在屏幕左上角等),只需在反转之前相应地修改你的掩码(添加任意正边缘像素或形状)并且 运行 距离 t运行变形。

我对您的代码进行了一些改动:我 运行 对彩色图像非常谨慎,因为就天空而言,它比灰度具有更好的对比度特性。我摆脱了你的关闭形态......我有点理解你为什么要这样做来关闭形状,但是因为我正在做一个距离 t运行sform 这一步是不必要的。

可以根据您的应用程序实施的一个额外步骤是,您可以保留最大点的值以及位置(确保不归一化)并且如果该值小于最大尺寸的一半你的文本(可能是宽度),然后文本将剪辑到对象 and/or 你可能想要抛出错误的图像边缘(或更改文本大小或其他东西)。

下面是生成上图的演示代码:

#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <string>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    bool debugFlag = true;
    std::string path = "C:/Local Software/voyDICOM/resources/images/cowboyPic.jpg";

    Mat originalImg = cv::imread(path, cv::IMREAD_COLOR);
    if (debugFlag) { imshow("orignal", originalImg); }

    Mat edged;
    cv::Canny(originalImg, edged, 150, 200); //canny runs just fine on color images (often better than grayscale depending)
    if (debugFlag) { imshow("edges", edged); }

    //set boundary pixels so text doesnt clip into image edge
    cv::rectangle(edged, Point(0,0), Point(edged.cols-1,edged.rows-1), 255,1);
    imshow("edges with boundary", edged);

    Mat inverted;//needs to be inverted for distance transform to work propperly
    bitwise_not(edged, inverted);

    Mat distMap;
    cv::distanceTransform(inverted, distMap, DIST_L1,3);
    if (debugFlag) 
    {
        //note that this normalization will limit your ability to enforce a fail case (if there is not sufficient room at best point)
        normalize(distMap, distMap, 0, 1.0, NORM_MINMAX); //this is only neccesary for visualization
        imshow("dist transform", distMap);
    }
    

    Point maxPt, minPt;
    double maxVal, minVal;
    minMaxLoc(distMap, &minVal, &maxVal, &minPt, &maxPt);

    //Estimate the size of the box so that you can offset the textbox from center point to top left corner.
    double fontScale = 1;
    int fontThick = 2;
    std::string sampleText = "TEST AREA";
    int baseline = 0;
    Size textSize = getTextSize(sampleText, FONT_HERSHEY_COMPLEX, fontScale, fontThick, &baseline);

    maxPt.x -= textSize.width/2;
    maxPt.y -= textSize.height/2;

    cv::putText(originalImg, "TEST AREA", maxPt, FONT_HERSHEY_COMPLEX, fontScale, Scalar(0, 0, 0), fontThick);
    imshow("Final", originalImg);
    
    waitKey(0);
}