[PS CC2024] Как растянуть изображение на весь холст?

ledzin

Топикстартер
10 лет на форуме
Сообщения
34
Реакции
0
Сканирую книгу, на которой иногда всю страницу занимает какое-нибудь фото. При кадрировании в Фотошопе выясняется, что фото не очень пропорционально, т.к. бывает, что при обрезке страниц в типографии они получаются не строго прямоугольными. Удаляю лишнее по краям слоя, и в итоге изображение выглядит как неправильный прямоугольник, вписанный в правильный :) Как заполнить этим изображением весь холст? Можно, конечно, применить Трансформирование > Искажение (или Деформация) и натянуть вручную. Но это вряд ли получится с точностью до пикселя, да и долго возиться.
Может, есть способ сделать это автоматически? В инете не нашла решения.
 
Проиллюстрируйте, пожалуйста. Гадание по фото всегда эффективнее, чем без оного.
 
неправильный прямоугольник, вписанный в правильный
Это прямо какое то эпохальное открытие в области геометрии со времен Лобачевского!
Я вот не помню, за геометрию Нобелевку не дают, это как математика считается? Если я ошибаюсь, то настоятельно советую подавать, у вас все шансы на получение!
 
  • Спасибо
Реакции: Любимцев
О, ошибка, конечно. :) Скорее непропорциональный четырехугольник, вписанный в прямоугольник
 
О, ошибка, конечно. :) Скорее непропорциональный четырехугольник, вписанный в прямоугольник
Нет такого понятия "непропорциональный четырехугольник". Любой прямоугольник "неправильный" и "непропорциональный" кроме квадрата, но вам же не квадрат нужен?
 
Полагаю речь идет о 4-хугольнике без прямых углов..
 
Возможно, речь о сканировании под неким углом, отличным от прямого, и попытках вручную исправить этот косяк?
 
На форуме Адоб была похожая тема: Transform corners of irregular rectangle into 90 degrees (посмотрите не только мой ответ, но и ответ c.pfaffenbichler)




Главная проблема - что есть правильные размеры исходного размера изображения.

В моем варианте размеры задаются фиксированно (переменные в начале кода), минимальными правками можно вместо них подставить размеры текущего документа (в пикселах), либо bounding box текущего слоя.
 
Последнее редактирование:
  • Спасибо
Реакции: George
Проиллюстрируйте, с терминологией у вас плохо. :( Понять вас сложно, легко это сделать неправильно.
 
file_001.jpg

Допустим, зеленый слой растянуть на весь холст так, чтобы он до пикселя совпал с желтым. Есть ли какая-нибудь команда, сочетание клавиш, чтоб это сделать, а не тянуть вручную за углы?
 
Выделять инструментом обрезки (не прямоугольником) нужно только зелёный.
 
Последнее редактирование:
Есть ли какая-нибудь команда, сочетание клавиш,
Шел 2024 год... люди все еще считали компьютер волшебной палочкой...
Но вообще есть. Работает через раз, но 50% вам поможет. Как раз для сканирования и предназначено:

1724061238072.png
 
Ну вот то же самое, скриптом (поправил вариант по ссылке выше, чтобы он использовал в качестве целевых размеры текущего документа):
JavaScript:
#target photoshop
var lr = new AM('layer'),
    pth = new AM('path'),
    doc = new AM('document');
lr.makeSelection();
lr.createPath(1)
var pthObj = lr.convertToObject(pth.getProperty('pathContents').value, 'pathComponents'),
    sourceRect = getCornerPoints(getPoints(pthObj));
lr.deleteCurrentPath()
lr.perspectiveWarpTransform(sourceRect.bounds, sourceRect.corners, getTargetRect([doc.getProperty('width'), doc.getProperty('height')]))
function getPoints(pth) {
    var points = [];
    for (a in pth) {
        var pc = pth[a];
        if (pc.subpathListKey) {
            for (b in pc.subpathListKey) {
                var slk = pc.subpathListKey[b]
                if (slk.closedSubpath) {
                    var pts = slk.points
                    for (c in pts) {
                        points.push({ x: pts[c].anchor.horizontal._value, y: pts[c].anchor.vertical._value })
                    }
                }
            }
        }
    }
    return points;
}
function getCornerPoints(points) {
    var boundingBox = {
        left: points.sort(function (a, b) { return a.x > b.x ? 1 : -1 })[0].x,
        right: points.sort(function (a, b) { return a.x < b.x ? 1 : -1 })[0].x,
        top: points.sort(function (a, b) { return a.y > b.y ? 1 : -1 })[0].y,
        bottom: points.sort(function (a, b) { return a.y < b.y ? 1 : -1 })[0].y
    }
    var corners = [
        points.sort(function (a, b) { return getLineLength(boundingBox.left, a.x, boundingBox.top, a.y) < getLineLength(boundingBox.left, b.x, boundingBox.top, b.y) ? 0 : 1 }).shift(),
        points.sort(function (a, b) { return getLineLength(boundingBox.right, a.x, boundingBox.top, a.y) < getLineLength(boundingBox.right, b.x, boundingBox.top, b.y) ? 0 : 1 }).shift(),
        points.sort(function (a, b) { return getLineLength(boundingBox.right, a.x, boundingBox.bottom, a.y) < getLineLength(boundingBox.right, b.x, boundingBox.bottom, b.y) ? 0 : 1 }).shift(),
        points.sort(function (a, b) { return getLineLength(boundingBox.left, a.x, boundingBox.bottom, a.y) < getLineLength(boundingBox.left, b.x, boundingBox.bottom, b.y) ? 0 : 1 }).shift()
    ]
    return { corners: corners, bounds: boundingBox }
    function getLineLength(x1, x2, y1, y2) {
        return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
    }
}
function getTargetRect(target) {
    return ([
        {
            x: 0,
            y: 0
        },
        {
            x: target[0],
            y: 0
        },
        {
            x: target[0],
            y: target[1],
        },
        {
            x: 0,
            y: target[1]
        }
    ]);
}
function AM(target, order) {
    var s2t = stringIDToTypeID,
        t2s = typeIDToStringID;
    target = s2t(target)
    this.getProperty = function (property, descMode, id, idxMode) {
        property = s2t(property);
        (r = new ActionReference()).putProperty(s2t('property'), property);
        id != undefined ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
            r.putEnumerated(target, s2t('ordinal'), order ? s2t(order) : s2t('targetEnum'));
        return descMode ? executeActionGet(r) : getDescValue(executeActionGet(r), property)
    }
    this.convertToObject = function (obj, key) {
        var d = new ActionDescriptor();
        switch (obj.typename) {
            case 'ActionList': d.putList(s2t(key), obj); break;
            case 'ActionDescriptor': d = obj; break;
        }
        (desc = new ActionDescriptor()).putObject(s2t('object'), s2t('json'), d);
        eval('var o = ' + executeAction(s2t('convertJSONdescriptor'), desc).getString(s2t('json')));
        return o[key]
    }
    this.makeSelection = function () {
        (r = new ActionReference()).putProperty(s2t('channel'), s2t('selection'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        r1 = new ActionReference();
        r1.putEnumerated(s2t('channel'), s2t('channel'), s2t('transparencyEnum'));
        d.putReference(s2t('to'), r1);
        executeAction(s2t('set'), d, DialogModes.NO);
    }
    this.deleteCurrentPath = function () {
        (r = new ActionReference()).putProperty(s2t('path'), s2t('workPath'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        executeAction(s2t('delete'), d, DialogModes.NO);
    }
    this.createPath = function (tolerance) {
        tolerance = tolerance ? tolerance : 10;
        (r = new ActionReference()).putClass(s2t('path'));
        (d = new ActionDescriptor()).putReference(s2t('null'), r);
        (r1 = new ActionReference()).putProperty(s2t('selectionClass'), s2t('selection'));
        d.putReference(s2t('from'), r1);
        d.putUnitDouble(s2t('tolerance'), s2t('pixelsUnit'), tolerance);
        executeAction(s2t('make'), d, DialogModes.NO);
    }
    this.perspectiveWarpTransform = function (referenceRect, corners, targetPoints) {
        (r = new ActionReference()).putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        d.putEnumerated(s2t("mode"), s2t("perspectiveWarpMode"), s2t("warp"));
        (d1 = new ActionDescriptor()).putUnitDouble(s2t("top"), s2t("pixelsUnit"), referenceRect.top);
        d1.putUnitDouble(s2t("left"), s2t("pixelsUnit"), referenceRect.left);
        d1.putUnitDouble(s2t("bottom"), s2t("pixelsUnit"), referenceRect.bottom);
        d1.putUnitDouble(s2t("right"), s2t("pixelsUnit"), referenceRect.right);
        d.putObject(s2t("referenceRect"), s2t("rectangle"), d1);
        var l = new ActionList();
        for (var i = 0; i < corners.length; i++) {
            var point = new ActionDescriptor();
            point.putUnitDouble(s2t("horizontal"), s2t("pixelsUnit"), corners[i].x);
            point.putUnitDouble(s2t("vertical"), s2t("pixelsUnit"), corners[i].y);
            l.putObject(s2t("point"), point);
        }
        d.putList(s2t("vertices"), l);
        var l = new ActionList(),
            l1 = new ActionList();
        for (var i = 0; i < targetPoints.length; i++) {
            var point = new ActionDescriptor();
            point.putUnitDouble(s2t("horizontal"), s2t("pixelsUnit"), targetPoints[i].x);
            point.putUnitDouble(s2t("vertical"), s2t("pixelsUnit"), targetPoints[i].y);
            l.putObject(s2t("point"), point);
            l1.putInteger(i);
        }
        d.putList(s2t("warpedVertices"), l);
        (d2 = new ActionDescriptor()).putList(s2t("indices"), l1);
        (l2 = new ActionList()).putObject(s2t("perspectiveWarpQuad"), d2);
        d.putList(s2t("quads"), l2);
        executeAction(s2t("perspectiveWarpTransform"), d, DialogModes.NO);
    }
    function getDescValue(d, p) {
        switch (d.getType(p)) {
            case DescValueType.OBJECTTYPE: return { type: t2s(d.getObjectType(p)), value: d.getObjectValue(p) };
            case DescValueType.LISTTYPE: return d.getList(p);
            case DescValueType.REFERENCETYPE: return d.getReference(p);
            case DescValueType.BOOLEANTYPE: return d.getBoolean(p);
            case DescValueType.STRINGTYPE: return d.getString(p);
            case DescValueType.INTEGERTYPE: return d.getInteger(p);
            case DescValueType.LARGEINTEGERTYPE: return d.getLargeInteger(p);
            case DescValueType.DOUBLETYPE: return d.getDouble(p);
            case DescValueType.ALIASTYPE: return d.getPath(p);
            case DescValueType.CLASSTYPE: return d.getClass(p);
            case DescValueType.UNITDOUBLE: return (d.getUnitDoubleValue(p));
            case DescValueType.ENUMERATEDTYPE: return { type: t2s(d.getEnumerationType(p)), value: t2s(d.getEnumerationValue(p)) };
            default: break;
        };
    }
}

Скрипту нужно понять где границы слоя, поэтому:
а) либо создать слой с нужным четерырехугольником
б) закомментировать пару строк кода и вручную расставить угловые точки

в теории можно найти границы изображения и средствами фотошопа, но это не будет корректно работать в 100% случаев
 
ТС, не парься, если не особо много, просто: просто CTRL+узел - растяни на нужный размер по каждому.