Ai
1 Star 0 Fork 0

coorrer/unipdf

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
morph.go 19.99 KB
一键复制 编辑 原始数据 按行查看 历史
xushuai 提交于 2025-09-23 15:05 +08:00 . init
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
package bitmap
import (
"gitee.com/coodder/unipdf/common"
"gitee.com/coodder/unipdf/internal/jbig2/errors"
)
// BoundaryCondition is the global enum variable used to define morph operation boundary conditions.
// More information about the definition could be found at: http://www.leptonica.org/binary-morphology.html#boundary-conditions
type BoundaryCondition int
const (
// AsymmetricMorphBC defines the asymmetric boundary condition for morph functions.
AsymmetricMorphBC BoundaryCondition = iota
// SymmetricMorphBC defines the symmetric boundary condition for morph funcftions.
SymmetricMorphBC
)
// MorphBC defines current morph boundary condition used by the morph functions.
// By default it is set to 'AsymetricMorphBC'.
var MorphBC BoundaryCondition
// MorphOperation is an enum that wraps the morph operations.
type MorphOperation int
// Enum morph operations.
const (
MopDilation MorphOperation = iota
MopErosion
MopOpening
MopClosing
MopRankBinaryReduction
MopReplicativeBinaryExpansion
MopAddBorder
)
// MorphProcess is the combination of the morph operator with it's values.
type MorphProcess struct {
Operation MorphOperation
Arguments []int
}
var intlogBase2 = [5]int{1, 2, 3, 0, 4}
func (p MorphProcess) verify(i int, netRed, border *int) error {
const processName = "MorphProcess.verify"
switch p.Operation {
case MopDilation, MopErosion, MopOpening, MopClosing:
if len(p.Arguments) != 2 {
return errors.Error(processName, "Operation: 'd', 'e', 'o', 'c' requires at least 2 arguments")
}
w, h := p.getWidthHeight()
if w <= 0 || h <= 0 {
return errors.Error(processName, "Operation: 'd', 'e', 'o', 'c' requires both width and height to be >= 0")
}
case MopRankBinaryReduction:
nred := len(p.Arguments)
*netRed += nred
if nred < 1 || nred > 4 {
return errors.Error(processName, "Operation: 'r' requires at least 1 and at most 4 arguments")
}
for i := 0; i < nred; i++ {
if p.Arguments[i] < 1 || p.Arguments[i] > 4 {
return errors.Error(processName, "RankBinaryReduction level must be in range (0, 4>")
}
}
case MopReplicativeBinaryExpansion:
if len(p.Arguments) == 0 {
return errors.Error(processName, "ReplicativeBinaryExpansion requires one argument")
}
fact := p.Arguments[0]
if fact != 2 && fact != 4 && fact != 8 {
return errors.Error(processName, "ReplicativeBinaryExpansion must be of factor in range {2,4,8}")
}
*netRed -= intlogBase2[fact/4]
case MopAddBorder:
if len(p.Arguments) == 0 {
return errors.Error(processName, "AddBorder requires one argument")
}
fact := p.Arguments[0]
if i > 0 {
return errors.Error(processName, "AddBorder must be a first morph process")
}
if fact < 1 {
return errors.Error(processName, "AddBorder value lower than 0")
}
*border = fact
}
return nil
}
func (p MorphProcess) getWidthHeight() (width, height int) {
return p.Arguments[0], p.Arguments[1]
}
// Centroid gets the centroid of the provided 'bm' bitmap.
// The parameters 'centTab' and 'sumTab' are optional.
// 'centTab' is a table for finding centroids.
// 'sumTab' is a table for finding pixel sums.
// The centorid point is relative to the UL corner.
func Centroid(bm *Bitmap, centTab, sumTab []int) (Point, error) {
return bm.centroid(centTab, sumTab)
}
// Centroids gets the centroids relative to the UL corner of each pix for the provided bitmaps.
func Centroids(bms []*Bitmap) (*Points, error) {
pta := make([]Point, len(bms))
centTab := makePixelCentroidTab8()
sumTab := makePixelSumTab8()
var err error
for i, bm := range bms {
pta[i], err = bm.centroid(centTab, sumTab)
if err != nil {
return nil, err
}
}
pts := Points(pta)
return &pts, nil
}
// DilateBrick dilate with all sel being hit.
func DilateBrick(d, s *Bitmap, hSize, vSize int) (*Bitmap, error) {
return dilateBrick(d, s, hSize, vSize)
}
// Dilate the source bitmap 's' using hits in the selection 'sel'.
// The 'd' destination bitmap is optional.
// The following cases are possible:
//
// 'd' == 's' the function writes the result back to 'src'.
// 'd' == nil the function creates new bitmap and writes the result to it.
// 'd' != 's' puts the results into existing 'd'.
func Dilate(d *Bitmap, s *Bitmap, sel *Selection) (*Bitmap, error) {
return dilate(d, s, sel)
}
// MakePixelSumTab8 creates table of integers that gives
// the number of 1 bits in the 8 bit index.
func MakePixelSumTab8() []int {
return makePixelSumTab8()
}
// MakePixelCentroidTab8 creates table of integers gives
// the centroid weight of the 1 bits in the 8 bit index.
func MakePixelCentroidTab8() []int {
return makePixelCentroidTab8()
}
// MorphSequence does the morph processes over the 'src' Bitmap with the provided sequence.
func MorphSequence(src *Bitmap, sequence ...MorphProcess) (*Bitmap, error) {
return morphSequence(src, sequence...)
}
func (b *Bitmap) centroid(centTab, sumTab []int) (Point, error) {
pt := Point{}
b.setPadBits(0)
if len(centTab) == 0 {
centTab = makePixelCentroidTab8()
}
if len(sumTab) == 0 {
sumTab = makePixelSumTab8()
}
var xsum, ysum, pixsum, rowsum, i, j int
var bt byte
for i = 0; i < b.Height; i++ {
line := b.RowStride * i
rowsum = 0
for j = 0; j < b.RowStride; j++ {
bt = b.Data[line+j]
if bt != 0 {
rowsum += sumTab[bt]
xsum += centTab[bt] + j*8*sumTab[bt]
}
}
pixsum += rowsum
ysum += rowsum * i
}
if pixsum != 0 {
pt.X = float32(xsum) / float32(pixsum)
pt.Y = float32(ysum) / float32(pixsum)
}
return pt, nil
}
var (
tabExpand2x = makeExpandTab2x()
tabExpand4x = makeExpandTab4x()
tabExpand8x = makeExpandTab8x()
)
// closeBitmap does two morph functions in a row - a dilate and erode.
func closeBitmap(d, s *Bitmap, sel *Selection) (*Bitmap, error) {
const processName = "closeBitmap"
var err error
if d, err = processMorphArgs2(d, s, sel); err != nil {
return nil, err
}
// dilate first
t, err := dilate(nil, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
// then erode
if _, err = erode(d, t, sel); err != nil {
return nil, errors.Wrap(err, processName, "")
}
return d, nil
}
func closeBrick(d, s *Bitmap, hSize, vSize int) (*Bitmap, error) {
const processName = "closeBrick"
if s == nil {
return nil, errors.Error(processName, "source not defined")
}
if hSize < 1 || vSize < 1 {
return nil, errors.Error(processName, "hSize and vSize not >= 1")
}
if hSize == 1 && vSize == 1 {
return s.Copy(), nil
}
if hSize == 1 || vSize == 1 {
sel := SelCreateBrick(vSize, hSize, vSize/2, hSize/2, SelHit)
var err error
d, err = closeBitmap(d, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "hSize == 1 || vSize == 1")
}
return d, nil
}
selH := SelCreateBrick(1, hSize, 0, hSize/2, SelHit)
selV := SelCreateBrick(vSize, 1, vSize/2, 0, SelHit)
t, err := dilate(nil, s, selH)
if err != nil {
return nil, errors.Wrap(err, processName, "1st dilate")
}
if d, err = dilate(d, t, selV); err != nil {
return nil, errors.Wrap(err, processName, "2nd dilate")
}
// erode
if _, err = erode(t, d, selH); err != nil {
return nil, errors.Wrap(err, processName, "1st erode")
}
if _, err = erode(d, t, selV); err != nil {
return nil, errors.Wrap(err, processName, "2nd erode")
}
return d, nil
}
func closeSafeBrick(d, s *Bitmap, hSize, vSize int) (*Bitmap, error) {
const processName = "closeSafeBrick"
if s == nil {
return nil, errors.Error(processName, "source is nil")
}
if hSize < 1 || vSize < 1 {
return nil, errors.Error(processName, "hsize and vsize not >= 1")
}
if hSize == 1 && vSize == 1 {
return copyBitmap(d, s)
}
if MorphBC == SymmetricMorphBC {
// Symmetric handles the pixels correctly
bm, err := closeBrick(d, s, hSize, vSize)
if err != nil {
return nil, errors.Wrap(err, processName, "SymmetricMorphBC")
}
return bm, nil
}
maxTrans := max(hSize/2, vSize/2)
bordSize := 8 * ((maxTrans + 7) / 8)
bsb, err := s.AddBorder(bordSize, 0)
if err != nil {
return nil, errors.Wrapf(err, processName, "BorderSize: %d", bordSize)
}
var bdb, t *Bitmap
if hSize == 1 || vSize == 1 {
sel := SelCreateBrick(vSize, hSize, vSize/2, hSize/2, SelHit)
bdb, err = closeBitmap(nil, bsb, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "hSize == 1 || vSize == 1")
}
} else {
selH := SelCreateBrick(1, hSize, 0, hSize/2, SelHit)
t, err := dilate(nil, bsb, selH)
if err != nil {
return nil, errors.Wrap(err, processName, "regular - first dilate")
}
selV := SelCreateBrick(vSize, 1, vSize/2, 0, SelHit)
bdb, err = dilate(nil, t, selV)
if err != nil {
return nil, errors.Wrap(err, processName, "regular - second dilate")
}
if _, err = erode(t, bdb, selH); err != nil {
return nil, errors.Wrap(err, processName, "regular - first erode")
}
if _, err = erode(bdb, t, selV); err != nil {
return nil, errors.Wrap(err, processName, "regular - second erode")
}
}
if t, err = bdb.RemoveBorder(bordSize); err != nil {
return nil, errors.Wrap(err, processName, "regular")
}
if d == nil {
return t, nil
}
if _, err = copyBitmap(d, t); err != nil {
return nil, err
}
return d, nil
}
func dilate(d *Bitmap, s *Bitmap, sel *Selection) (*Bitmap, error) {
var (
t *Bitmap
err error
)
d, err = processMorphArgs1(d, s, sel, &t)
if err != nil {
return nil, err
}
if err = d.clearAll(); err != nil {
return nil, err
}
var selData SelectionValue
for i := 0; i < sel.Height; i++ {
for j := 0; j < sel.Width; j++ {
selData = sel.Data[i][j]
if selData == SelHit {
if err = d.RasterOperation(j-sel.Cx, i-sel.Cy, s.Width, s.Height, PixSrcOrDst, t, 0, 0); err != nil {
return nil, err
}
}
}
}
return d, nil
}
// hSize is the width of the brick sel
// vSize is the height of the brick sel
func dilateBrick(d, s *Bitmap, hSize, vSize int) (*Bitmap, error) {
const processName = "dilateBrick"
if s == nil {
common.Log.Debug("dilateBrick source not defined")
return nil, errors.Error(processName, "source bitmap not defined")
}
if hSize < 1 || vSize < 1 {
return nil, errors.Error(processName, "hSzie and vSize are no greater equal to 1")
}
if hSize == 1 && vSize == 1 {
bm, err := copyBitmap(d, s)
if err != nil {
return nil, errors.Wrap(err, processName, "hSize == 1 && vSize == 1")
}
return bm, nil
}
if hSize == 1 || vSize == 1 {
sel := SelCreateBrick(vSize, hSize, vSize/2, hSize/2, SelHit)
d, err := dilate(d, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "hsize == 1 || vSize == 1")
}
return d, nil
}
selH := SelCreateBrick(1, hSize, 0, hSize/2, SelHit)
selV := SelCreateBrick(vSize, 1, vSize/2, 0, SelHit)
bmT, err := dilate(nil, s, selH)
if err != nil {
return nil, errors.Wrap(err, processName, "1st dilate")
}
d, err = dilate(d, bmT, selV)
if err != nil {
return nil, errors.Wrap(err, processName, "2nd dilate")
}
return d, nil
}
func erode(d, s *Bitmap, sel *Selection) (*Bitmap, error) {
const processName = "erode"
var (
err error
t *Bitmap
)
d, err = processMorphArgs1(d, s, sel, &t)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
if err = d.setAll(); err != nil {
return nil, errors.Wrap(err, processName, "")
}
var selData SelectionValue
for i := 0; i < sel.Height; i++ {
for j := 0; j < sel.Width; j++ {
selData = sel.Data[i][j]
if selData == SelHit {
err = rasterOperation(d, sel.Cx-j, sel.Cy-i, s.Width, s.Height, PixSrcAndDst, t, 0, 0)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
}
}
}
if MorphBC == SymmetricMorphBC {
return d, nil
}
// If the MorphBoundaryCondition is set to the AsymmetricMorphBC
// then the erode function should set the pixels surrounding the image to 'OFF'.
xp, yp, xn, yn := sel.findMaxTranslations()
if xp > 0 {
if err = d.RasterOperation(0, 0, xp, s.Height, PixClr, nil, 0, 0); err != nil {
return nil, errors.Wrap(err, processName, "xp > 0")
}
}
if xn > 0 {
if err = d.RasterOperation(s.Width-xn, 0, xn, s.Height, PixClr, nil, 0, 0); err != nil {
return nil, errors.Wrap(err, processName, "xn > 0")
}
}
if yp > 0 {
if err = d.RasterOperation(0, 0, s.Width, yp, PixClr, nil, 0, 0); err != nil {
return nil, errors.Wrap(err, processName, "yp > 0")
}
}
if yn > 0 {
if err = d.RasterOperation(0, s.Height-yn, s.Width, yn, PixClr, nil, 0, 0); err != nil {
return nil, errors.Wrap(err, processName, "yn > 0")
}
}
return d, nil
}
func erodeBrick(d, s *Bitmap, hSize, vSize int) (*Bitmap, error) {
const processName = "erodeBrick"
if s == nil {
return nil, errors.Error(processName, "source not defined")
}
if hSize < 1 || vSize < 1 {
return nil, errors.Error(processName, "hsize and vsize are not greater than or equal to 1")
}
if hSize == 1 && vSize == 1 {
bm, err := copyBitmap(d, s)
if err != nil {
return nil, errors.Wrap(err, processName, "hSize == 1 && vSize == 1")
}
return bm, nil
}
if hSize == 1 || vSize == 1 {
sel := SelCreateBrick(vSize, hSize, vSize/2, hSize/2, SelHit)
d, err := erode(d, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "hSize == 1 || vSize == 1")
}
return d, nil
}
selH := SelCreateBrick(1, hSize, 0, hSize/2, SelHit)
selV := SelCreateBrick(vSize, 1, vSize/2, 0, SelHit)
bmT, err := erode(nil, s, selH)
if err != nil {
return nil, errors.Wrap(err, processName, "1st erode")
}
d, err = erode(d, bmT, selV)
if err != nil {
return nil, errors.Wrap(err, processName, "2nd erode")
}
return d, nil
}
func expandReplicate(s *Bitmap, factor int) (*Bitmap, error) {
const processName = "expandReplicate"
if s == nil {
return nil, errors.Error(processName, "source not defined")
}
if factor <= 0 {
return nil, errors.Error(processName, "invalid factor - <= 0")
}
if factor == 1 {
bm, err := copyBitmap(nil, s)
if err != nil {
return nil, errors.Wrap(err, processName, "factor = 1")
}
return bm, nil
}
bm, err := expandBinaryReplicate(s, factor, factor)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
return bm, nil
}
func makePixelCentroidTab8() []int {
tab := make([]int, 256)
tab[0] = 0
tab[1] = 7
var i int
for i = 2; i < 4; i++ {
tab[i] = tab[i-2] + 6
}
for i = 4; i < 8; i++ {
tab[i] = tab[i-4] + 5
}
for i = 8; i < 16; i++ {
tab[i] = tab[i-8] + 4
}
for i = 16; i < 32; i++ {
tab[i] = tab[i-16] + 3
}
for i = 32; i < 64; i++ {
tab[i] = tab[i-32] + 2
}
for i = 64; i < 128; i++ {
tab[i] = tab[i-64] + 1
}
for i = 128; i < 256; i++ {
tab[i] = tab[i-128]
}
return tab
}
func makePixelSumTab8() []int {
tab := make([]int, 256)
for i := 0; i <= 0xff; i++ {
i := byte(i)
tab[i] = int(i&0x1) + (int(i>>1) & 0x1) + (int(i>>2) & 0x1) + (int(i>>3) & 0x1) +
(int(i>>4) & 0x1) + (int(i>>5) & 0x1) + (int(i>>6) & 0x1) + (int(i>>7) & 0x1)
}
return tab
}
func morphSequence(s *Bitmap, sequence ...MorphProcess) (d *Bitmap, err error) {
const processName = "morphSequence"
if s == nil {
return nil, errors.Error(processName, "morphSequence source bitmap not defined")
}
if len(sequence) == 0 {
return nil, errors.Error(processName, "morphSequence, sequence not defined")
}
if err = verifyMorphProcesses(sequence...); err != nil {
return nil, errors.Wrap(err, processName, "")
}
var w, h, border int
d = s.Copy()
for _, process := range sequence {
switch process.Operation {
case MopDilation:
// 'd' character
w, h = process.getWidthHeight()
d, err = DilateBrick(nil, d, w, h)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
case MopErosion:
// 'e' character
w, h = process.getWidthHeight()
d, err = erodeBrick(nil, d, w, h)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
case MopOpening:
// 'o' character
w, h = process.getWidthHeight()
d, err = openBrick(nil, d, w, h)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
case MopClosing:
// 'c' character
w, h = process.getWidthHeight()
d, err = closeSafeBrick(nil, d, w, h)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
case MopRankBinaryReduction:
// 'r' character
d, err = reduceRankBinaryCascade(d, process.Arguments...)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
case MopReplicativeBinaryExpansion:
// 'x' character
d, err = expandReplicate(d, process.Arguments[0])
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
case MopAddBorder:
// 'b' character
border = process.Arguments[0]
d, err = d.AddBorder(border, 0)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
default:
return nil, errors.Error(processName, "invalid morphOperation provided to the sequence")
}
}
if border > 0 {
d, err = d.RemoveBorder(border)
if err != nil {
return nil, errors.Wrap(err, processName, "border > 0")
}
}
return d, nil
}
// open is the function based on the leptonica 'pixOpen' - morph.c:422
// this function does a sequence of erode and dilate morph operations.
func open(d, s *Bitmap, sel *Selection) (*Bitmap, error) {
const processName = "open"
var err error
d, err = processMorphArgs2(d, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
t, err := erode(nil, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
_, err = dilate(d, t, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
return d, nil
}
func openBrick(d, s *Bitmap, hSize, vSize int) (*Bitmap, error) {
const processName = "openBrick"
if s == nil {
return nil, errors.Error(processName, "source bitmap not defined")
}
if hSize < 1 && vSize < 1 {
return nil, errors.Error(processName, "hSize < 1 && vSize < 1")
}
if hSize == 1 && vSize == 1 {
return s.Copy(), nil
}
if hSize == 1 || vSize == 1 {
var err error
sel := SelCreateBrick(vSize, hSize, vSize/2, hSize/2, SelHit)
d, err = open(d, s, sel)
if err != nil {
return nil, errors.Wrap(err, processName, "hSize == 1 || vSize == 1")
}
return d, nil
}
selH := SelCreateBrick(1, hSize, 0, hSize/2, SelHit)
selV := SelCreateBrick(vSize, 1, vSize/2, 0, SelHit)
t, err := erode(nil, s, selH)
if err != nil {
return nil, errors.Wrap(err, processName, "1st erode")
}
d, err = erode(d, t, selV)
if err != nil {
return nil, errors.Wrap(err, processName, "2nd erode")
}
_, err = dilate(t, d, selH)
if err != nil {
return nil, errors.Wrap(err, processName, "1st dilate")
}
_, err = dilate(d, t, selV)
if err != nil {
return nil, errors.Wrap(err, processName, "2nd dilate")
}
return d, nil
}
// processMorphArgs1 used for generic erosion, dilation and HMT.
func processMorphArgs1(d *Bitmap, s *Bitmap, sel *Selection, t **Bitmap) (*Bitmap, error) {
const processName = "processMorphArgs1"
if s == nil {
return nil, errors.Error(processName, "MorphArgs1 's' not defined")
}
if sel == nil {
return nil, errors.Error(processName, "MorhpArgs1 'sel' not defined")
}
sx, sy := sel.Height, sel.Width
if sx == 0 || sy == 0 {
return nil, errors.Error(processName, "selection of size 0")
}
if d == nil {
d = s.createTemplate()
*t = s
return d, nil
}
d.Width = s.Width
d.Height = s.Height
d.RowStride = s.RowStride
d.Color = s.Color
d.Data = make([]byte, s.RowStride*s.Height)
if d == s {
*t = s.Copy()
} else {
*t = s
}
return d, nil
}
func processMorphArgs2(d, s *Bitmap, sel *Selection) (*Bitmap, error) {
const processName = "processMorphArgs2"
var sx, sy int
if s == nil {
return nil, errors.Error(processName, "source bitmap is nil")
}
if sel == nil {
return nil, errors.Error(processName, "sel not defined")
}
sx = sel.Width
sy = sel.Height
if sx == 0 || sy == 0 {
return nil, errors.Error(processName, "sel of size 0")
}
if d == nil {
return s.createTemplate(), nil
}
if err := d.resizeImageData(s); err != nil {
return nil, err
}
return d, nil
}
func verifyMorphProcesses(processes ...MorphProcess) (err error) {
const processName = "verifyMorphProcesses"
var netRed, border int
for i, process := range processes {
if err = process.verify(i, &netRed, &border); err != nil {
return errors.Wrap(err, processName, "")
}
}
if border != 0 && netRed != 0 {
return errors.Error(processName, "Morph sequence - border added but net reduction not 0")
}
return nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/coorrer/unipdf.git
git@gitee.com:coorrer/unipdf.git
coorrer
unipdf
unipdf
v1.2.0

搜索帮助